Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2018-05-05 12:25:11 -04:00
commit f6139d33ed
130 changed files with 2792 additions and 2756 deletions

4
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "web/api/app/Plugin/Crud"] [submodule "web/api/app/Plugin/Crud"]
path = web/api/app/Plugin/Crud path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git url = https://github.com/ZoneMinder/crud.git
branch = 3.0 branch = 3.0
[submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"] [submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"]
path = web/api/app/Plugin/CakePHP-Enum-Behavior path = web/api/app/Plugin/CakePHP-Enum-Behavior
url = https://github.com/connortechnology/CakePHP-Enum-Behavior.git url = https://github.com/ZoneMinder/CakePHP-Enum-Behavior.git

View File

@ -26,6 +26,8 @@ addons:
- binfmt-support - binfmt-support
- qemu - qemu
- qemu-user-static - qemu-user-static
- dnsutils
- traceroute
install: install:
- update-binfmts --enable qemu-arm - update-binfmts --enable qemu-arm
@ -33,8 +35,6 @@ env:
global: global:
- SMPFLAGS=-j4 - SMPFLAGS=-j4
matrix: matrix:
- OS=el DIST=6
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
- OS=el DIST=7 - OS=el DIST=7
- OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack - OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack
- OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack - OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack

View File

@ -143,6 +143,8 @@ set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www"
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www") "Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") "Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
"Location of the web server cache busting files, default: /var/cache/zoneminder")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
"Location of dynamic content (events and images), default: /var/lib/zoneminder") "Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_DB_HOST "localhost" CACHE STRING set(ZM_DB_HOST "localhost" CACHE STRING
@ -207,7 +209,7 @@ set(ZM_PERL_SEARCH_PATH "" CACHE PATH
where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are
installed outside Perl's default search path.") installed outside Perl's default search path.")
set(ZM_TARGET_DISTRO "" CACHE STRING set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: fc24, fc25, el6, el7, OS13, FreeBSD") "Build ZoneMinder for a specific distribution. Currently, valid names are: fc27, fc26, el7, OS13, FreeBSD")
set(ZM_SYSTEMD "OFF" CACHE BOOL set(ZM_SYSTEMD "OFF" CACHE BOOL
"Set to ON to force building ZM with systemd support. default: OFF") "Set to ON to force building ZM with systemd support. default: OFF")

View File

@ -1,7 +1,7 @@
ZoneMinder ZoneMinder
========== ==========
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) [![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org

View File

@ -2,6 +2,7 @@
# Create files from the .in files # Create files from the .in files
configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY) configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY)
configure_file(zm_update-1.31.30.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" @ONLY)
# Glob all database upgrade scripts # Glob all database upgrade scripts
file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql") file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
@ -9,8 +10,12 @@ file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
# Install the database upgrade scripts # Install the database upgrade scripts
install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_update-1.31.30.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_create.sql # install zm_create.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install triggers.sql # install triggers.sql
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")

View File

@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`;
CREATE TABLE `Controls` ( CREATE TABLE `Controls` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local', `Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL','WebSite') NOT NULL default 'Local',
`Protocol` varchar(64) default NULL, `Protocol` varchar(64) default NULL,
`CanWake` tinyint(3) unsigned NOT NULL default '0', `CanWake` tinyint(3) unsigned NOT NULL default '0',
`CanSleep` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0',
@ -500,6 +500,7 @@ CREATE TABLE `Monitors` (
`DefaultView` enum('Events','Control') NOT NULL default 'Events', `DefaultView` enum('Events','Control') NOT NULL default 'Events',
`DefaultRate` smallint(5) unsigned NOT NULL default '100', `DefaultRate` smallint(5) unsigned NOT NULL default '100',
`DefaultScale` smallint(5) unsigned NOT NULL default '100', `DefaultScale` smallint(5) unsigned NOT NULL default '100',
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
`WebColour` varchar(32) NOT NULL default 'red', `WebColour` varchar(32) NOT NULL default 'red',
`Exif` tinyint(1) unsigned NOT NULL default '0', `Exif` tinyint(1) unsigned NOT NULL default '0',
@ -528,6 +529,7 @@ CREATE TABLE `Monitor_Status` (
`Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0, `CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
`CaptureBandwidth` INT NOT NULL default 0,
PRIMARY KEY (`MonitorId`) PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY; ) ENGINE=MEMORY;
-- --
@ -714,7 +716,7 @@ CREATE TABLE `Storage` (
-- --
-- Create a default storage location -- Create a default storage location
-- --
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default', 'local', NULL, 'Medium', 0 ); insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true );
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
@ -891,7 +893,7 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' ); INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts. -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @ZM_PATH_DATA@/db/triggers.sql source @PKGDATADIR@/db/triggers.sql
-- --
-- Apply the initial configuration -- Apply the initial configuration
-- --

View File

@ -0,0 +1,20 @@
DROP TABLE IF EXISTS `Monitor_Status`;
CREATE TABLE `Monitor_Status` (
`MonitorId` int(10) unsigned NOT NULL,
`Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY;
SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO';
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM Storage WHERE Name = 'Default' AND Id=0 AND Path='@ZM_DIR_EVENTS@'
) > 0,
"SELECT 'Default Storage Area already exists.'",
"INSERT INTO Storage (Id,Name,Path,Scheme,ServerId) VALUES (0,'Default','@ZM_DIR_EVENTS@','Medium',NULL)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.31.42.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'SignalCheckPoints'
) > 0,
"SELECT 'Column SignalCheckPoints already exists in Storage'",
"ALTER TABLE `Monitors` ADD `SignalCheckPoints` INT UNSIGNED NOT NULL default '0' AFTER `DefaultScale`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

24
db/zm_update-1.31.43.sql Normal file
View File

@ -0,0 +1,24 @@
--
-- This updates a 1.31.42 database to 1.31.43
--
-- Add WebSite enum to Monitor.Type
-- Add Refresh column to Monitors table
--
ALTER TABLE `zm`.`Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.31.44.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitor_Status'
AND column_name = 'CaptureBandwidth'
) > 0,
"SELECT 'Column CaptureBandwidth already exists in Monitor_Status'",
"ALTER TABLE `Monitor_Status` ADD `CaptureBandwidth` INT NOT NULL default 0 AFTER `AnalysisFPS`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -14,24 +14,18 @@ if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
# Configure the zoneminder service files # Configure the zoneminder service files
if(ZM_TARGET_DISTRO STREQUAL "el6") configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY) if(ZM_WEB_USER STREQUAL "nginx")
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
else(ZM_TARGET_DISTRO STREQUAL "el6") configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) endif(ZM_WEB_USER STREQUAL "nginx")
if(ZM_WEB_USER STREQUAL "nginx")
configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
endif(ZM_WEB_USER STREQUAL "nginx")
endif(ZM_TARGET_DISTRO STREQUAL "el6")
# Unpack jscalendar & move files into position # Unpack jscalendar & move files into position
message(STATUS "Unpacking and Installing jscalendar...") message(STATUS "Unpacking and Installing jscalendar...")
@ -52,6 +46,7 @@ file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/cache DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
@ -61,23 +56,18 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminde
# Link to Cambozola # Link to Cambozola
install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")")
# Install auxiliary files required to run zoneminder on CentOS # Install auxiliary files
install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
# Install zoneminder service files
install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
if(ZM_WEB_USER STREQUAL "nginx") if(ZM_WEB_USER STREQUAL "nginx")
install(FILES zoneminder.conf DESTINATION /etc/nginx/default.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf) install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf)
else(ZM_WEB_USER STREQUAL "nginx")
install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
endif(ZM_WEB_USER STREQUAL "nginx") endif(ZM_WEB_USER STREQUAL "nginx")
if(ZM_TARGET_DISTRO STREQUAL "el6") install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.sysvinit DESTINATION /etc/rc.d/init.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
else(ZM_TARGET_DISTRO STREQUAL "el6")
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
endif(ZM_TARGET_DISTRO STREQUAL "el6")

View File

@ -9,6 +9,23 @@ RewriteEngine On
RewriteCond %{HTTPS} !=on RewriteCond %{HTTPS} !=on
RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L] RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L]
# Order matters. This alias must come first.
Alias /zm/cache "@ZM_CACHEDIR@"
<Directory "@ZM_CACHEDIR@">
SSLRequireSSL
Options -Indexes +MultiViews +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
Alias /zm "@ZM_WEBDIR@" Alias /zm "@ZM_WEBDIR@"
<Directory "@ZM_WEBDIR@"> <Directory "@ZM_WEBDIR@">
# explicitly set index.php as the only directoryindex # explicitly set index.php as the only directoryindex

View File

@ -1,37 +1,19 @@
What's New What's New
========== ==========
1. ZoneMinder now uses a conf.d subfolder to process custom changes to 1. See the ZoneMinder release notes for a list of new features:
variables found in zm.conf. Changes to zm.conf will be overwritten https://github.com/ZoneMinder/zoneminder/releases
during an upgrade. Instead, create a file with a ".conf" extension under
the conf.d folder and make your changes there.
2. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done to avoid this bug:
https://bugzilla.redhat.com/show_bug.cgi?id=973067
IMPORTANT: You must manually inspect the value for PATH_ZMS under Options
and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
4. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
New installs New installs
============ ============
1. Unless you are already using MariaDB server, you need to ensure that the 1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running: server is configured to start during boot and properly secured by running:
sudo dnf install mariadb-server sudo dnf install mariadb-server
sudo systemctl enable mariadb sudo systemctl enable mariadb
sudo systemctl start mariadb.service sudo systemctl start mariadb.service
mysql_secure_installation mysql_secure_installation
2. Assuming the database is local and using the password for the root account 2. Assuming the database is local and using the password for the root account
set during the previous step, you will need to create the ZoneMinder set during the previous step, you will need to create the ZoneMinder
@ -50,13 +32,13 @@ New installs
/etc/zm/conf.d and set your credentials there. For example, create the file /etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it: /etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use} ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use} ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it: Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf sudo chown root:apache *.conf
sudo chmod 640 *.conf sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -82,34 +64,62 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot. effect after a reboot.
6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your 6. Configure the web server
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo dnf install mod_ssl This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
If this does not meet your needs, then read README.https to Inspect the web server configuration file and verify it meets your needs:
learn about alternatives. When in doubt, install mod_ssl.
/etc/zm/www/zoneminder.conf
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
When in doubt, proceed with the default:
sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
sudo dnf install mod_ssl
7. Now start the web server: 7. Now start the web server:
sudo systemctl enable httpd sudo systemctl enable httpd
sudo systemctl start httpd sudo systemctl start httpd
8. Now start zoneminder: 8. Now start zoneminder:
sudo systemctl enable zoneminder sudo systemctl enable zoneminder
sudo systemctl start zoneminder sudo systemctl start zoneminder
9. The Fedora repos have a ZoneMinder package available, but it does not 9. Optionally configure the firewall
support ffmpeg or libvlc, which many modern IP cameras require. Most users
will want to prevent the ZoneMinder package in the Fedora repos from All Redhat distros ship with the firewall enabled. That means you will not
overwriting the ZoneMinder package in zmrepo, during a future dnf update. To be able to access the ZoneMinder web console from a remote machine until
prevent that from happening you must edit /etc/yum.repos.d/fedora.repo changes are made to the firewall.
and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*"
without the quotes under the [fedora] and [fedora-updates] blocks, What follows are a set of minimal commands to allow remote access to the
respectively. ZoneMinder web console and also allow ZoneMinder's ONVIF discovery to
work. The following commands do not put any restrictions on which remote
machine(s) have access to the listed ports or services.
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --permanent --zone=public --add-port=3702/udp
sudo firewall-cmd --reload
Additional changes to the firewall may be required, depending on your
security requirements and how you use the system. It is up to you to verify
these commands are sufficient.
10. Access the ZoneMinder web console
You may now access the ZoneMinder web console from your web browser using
an appropriate url. Here are some examples:
http://localhost/zm (works from the local machine only)
http://{machine name}/zm (works only if dns is configured for your network)
http://{ip address}/zm
Upgrades Upgrades
======== ========
@ -131,7 +141,7 @@ Upgrades
See step 2 of the Installation section to add missing permissions. See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder 3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there /etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf. exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary. Verify the SSL REquirements meet your needs. Read README.https if necessary.

View File

@ -1,161 +0,0 @@
What's New
==========
1. ***EOL NOTICE***
It has become increasingly difficult to maintain the ZoneMinder project such
that it remains compatible with EL6 distros. The version of php shipped with
EL6 distros and the version of ffmpeg which will build against EL6 are too
old. It is with regret that I must announce our plans to stop supporting
ZoneMinder on EL6 distros soon. Your best option is to upgrade to an EL7
distro or another distro with newer php & ffmpeg packages. Please note that
replacing core packages, such as php, will not be supported by us. You are
on your own should you choose to go down that path.
2. ZoneMinder now uses a conf.d subfolder to process custom changes to
variables found in zm.conf. Changes to zm.conf will be overwritten
during an upgrade. Instead, create a file with a ".conf" extension under
th2 conf.d folder and make your changes there.
3. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
4. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done match the configuration of
CentOS7/Fedora and simplify the build process.
IMPORTANT: You must manually verify the value of PATH_ZMS under Options.
Make sure it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
5. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
6. The php package that ships with CentOS 6 does not support the new ZoneMinder
API. If you require API functionality (such as using a mobile app) then you
should consider an upgrade to CentOS 7 or use Fedora.
New installs
============
1. Unless you are already using MySQL server, you need to ensure that
the server is confired to start during boot and properly secured
by running:
sudo yum install mysql-server
sudo service mysqld start
/usr/bin/mysql_secure_installation
sudo chkconfig mysqld on
2. Using the password for the root account set during the previous step, you
will need to create the ZoneMinder database and configure a database
account for ZoneMinder to use:
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
mysql -uroot -p -e "grant all on zm.* to \
'zmuser'@localhost identified by 'zmpass';"
mysqladmin -uroot -p reload
The database account credentials, zmuser/zmpass, are arbitrary. Set them to
anything that suits your environment.
3. If you have chosen to change the zoneminder database account credentials to
something other than zmuser/zmpass, you must now create a config file under
/etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf
sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set
incorrectly, and these complaints will show up in the zoneminder logging
system as errors
If you are not sure of the proper timezone specification to use, look at
http://php.net/date.timezone
5. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo yum install mod_ssl
If this does not meet your needs, then read README.https to
learn about alternatives. When in doubt, install mod_ssl.
6. Configure the web server to start automatically:
sudo chkconfig httpd on
sudo service httpd start
7. This package will automatically configure and install an SELinux policy
called local_zoneminder. A copy of this policy is in the documentation
folder.
It is still possible to run into SELinux issues, however. If this is case,
you can disable SELinux permanently by editing the following:
/etc/selinux/conf
Change SELINUX line from "enforcing" to "disabled". This change will not
take effect until a reboot, however. To avoid a reboot, execute the
following from the commandline:
sudo setenforce 0
8. Finally, you may start the ZoneMinder service:
sudo service zoneminder start
Then point your web browser to http://<machine name or ip>/zm
Upgrades
========
1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
under the conf.d folder, they will remain in place indefinitely.
2. Verify permissions of the zmuser account.
Over time, the database account permissions required for normal operation
have increased. Verify the zmuser database account has been granted all
permission to the ZoneMinder database:
mysql -uroot -p -e "show grants for zmuser@localhost;"
See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary.
4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command:
sudo zmupdate.pl
Recent versions of ZoneMinder don't require any parameters added to the
zmupdate command. However, if ZoneMinder complains, you may need to call
zmupdate in the following manner:
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now restart the web server then start zoneminder:
sudo service httpd restart
sudo service zoneminder start

View File

@ -1,26 +1,8 @@
What's New What's New
========== ==========
1. ZoneMinder now uses a conf.d subfolder to process custom changes to 1. See the ZoneMinder release notes for a list of new features:
variables found in zm.conf. Changes to zm.conf will be overwritten https://github.com/ZoneMinder/zoneminder/releases
during an upgrade. Instead, create a file with a ".conf" extension under
the conf.d folder and make your changes there.
2. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done to avoid this bug:
https://bugzilla.redhat.com/show_bug.cgi?id=973067
IMPORTANT: You must manually inspect the value for PATH_ZMS under Options
and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
4. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
New installs New installs
============ ============
@ -28,10 +10,10 @@ New installs
1. Unless you are already using MariaDB server, you need to ensure that the 1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running: server is configured to start during boot and properly secured by running:
sudo yum install mariadb-server sudo yum install mariadb-server
sudo systemctl enable mariadb sudo systemctl enable mariadb
sudo systemctl start mariadb.service sudo systemctl start mariadb.service
mysql_secure_installation mysql_secure_installation
2. Using the password for the root account set during the previous step, you 2. Using the password for the root account set during the previous step, you
will need to create the ZoneMinder database and configure a database will need to create the ZoneMinder database and configure a database
@ -50,13 +32,13 @@ New installs
/etc/zm/conf.d and set your credentials there. For example, create the file /etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it: /etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use} ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use} ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it: Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf sudo chown root:apache *.conf
sudo chmod 640 *.conf sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -82,25 +64,62 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot. effect after a reboot.
6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your 6. Configure the web server
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo yum install mod_ssl This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
If this does not meet your needs, then read README.https to Inspect the web server configuration file and verify it meets your needs:
learn about alternatives. When in doubt, install mod_ssl.
/etc/zm/www/zoneminder.conf
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
When in doubt, proceed with the default:
sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
sudo dnf install mod_ssl
7. Now start the web server: 7. Now start the web server:
sudo systemctl enable httpd sudo systemctl enable httpd
sudo systemctl start httpd sudo systemctl start httpd
8. Now start zoneminder: 8. Now start zoneminder:
sudo systemctl enable zoneminder sudo systemctl enable zoneminder
sudo systemctl start zoneminder sudo systemctl start zoneminder
9. Optionally configure the firewall
All Redhat distros ship with the firewall enabled. That means you will not
be able to access the ZoneMinder web console from a remote machine until
changes are made to the firewall.
What follows are a set of minimal commands to allow remote access to the
ZoneMinder web console and also allow ZoneMinder's ONVIF discovery to
work. The following commands do not put any restrictions on which remote
machine(s) have access to the listed ports or services.
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --permanent --zone=public --add-port=3702/udp
sudo firewall-cmd --reload
Additional changes to the firewall may be required, depending on your
security requirements and how you use the system. It is up to you to verify
these commands are sufficient.
10. Access the ZoneMinder web console
You may now access the ZoneMinder web console from your web browser using
an appropriate url. Here are some examples:
http://localhost/zm (works from the local machine only)
http://{machine name}/zm (works only if dns is configured for your network)
http://{ip address}/zm
Upgrades Upgrades
======== ========
@ -122,7 +141,7 @@ Upgrades
See step 2 of the Installation section to add missing permissions. See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder 3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there /etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf. exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary. Verify the SSL REquirements meet your needs. Read README.https if necessary.

View File

@ -1,29 +1,26 @@
HTTPS is now a requirement HTTPS is now the default
========================== ========================
This package now depends on Apache's mod_ssl package. This will automatically By default, ZoneMinder will use the certifciate created when the mod_ssl
be installed along with ZoneMinder. Upon installation, the mod_ssl package pacakge was installed on your system.
will create a default, self-signed certificate. This is the certificate that
ZoneMinder will use out of the box.
Since the certificate is self-signed, you will get a warning from your browser Since the certificate is self-signed, you will get a warning from your browser
the first time you access the web portal. This is normal. the first time you access the web portal. This is normal.
This is not intended to be an all encompasing solution for everyone. ZoneMinder This is not intended to be an all encompasing solution for everyone. ZoneMinder
will work just fine over HTTPS the way it is currently configured. However, will work just fine over HTTPS the way it is currently configured. However,
here are a couple of considerations you may want to take. here are a couple of considerations you may want to take to improve your
experience.
1. Create your own certificate. The CentOS wiki has a guide that describes how 1. Install a fully signed certificate from letsencrypt, using certbot. See the
certbot site for more information. This free service is very easy to set up.
https://certbot.eff.org/all-instructions/
2. Create your own certificate. The CentOS wiki has a guide that describes how
to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling
"centos certificate" reveals many articles on the subject. Note that some "centos certificate" reveals many articles on the subject.
third party applications, such as zmNinja, will require you to create a
certificate different than the default certificate on your machine.
2. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL 3. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL
directives found in /etc/httpd/conf.d/zoneminder.conf. You should also directives found in /etc/httpd/conf.d/zoneminder.conf. You should also
comment out the HTTP -> HTTPS Rewrite rule. comment out the HTTP -> HTTPS Rewrite rule.
3. Install a fully signed certificate from letsencrypt. See the Letsencrypt
site for more information. https://letsencrypt.org/
This service is totally free!

View File

@ -1,2 +1,5 @@
D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_CACHEDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_DIR_EVENTS@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_DIR_IMAGES@ 0755 @WEB_USER@ @WEB_GROUP@

View File

@ -1,121 +0,0 @@
#!/bin/sh
# description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008
# chkconfig: - 99 00
# processname: zmpkg.pl
# Source function library.
. /etc/rc.d/init.d/functions
prog=ZoneMinder
ZM_CONFIG="@ZM_CONFIG@"
pidfile="@ZM_RUNDIR@"
LOCKFILE=/var/lock/subsys/zm
loadconf()
{
if [ -f $ZM_CONFIG ]; then
. $ZM_CONFIG
else
echo "ERROR: $ZM_CONFIG not found."
return 1
fi
}
loadconf
command="$ZM_PATH_BIN/zmpkg.pl"
start()
{
# Commenting out as it is not needed. Leaving as a placeholder for future use.
# zmupdate || return $?
loadconf || return $?
#Make sure the directory for our PID folder exists or create one.
[ ! -d $pidfile ] \
&& mkdir -m 774 $pidfile \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile
#Make sure the folder for the socks file exists or create one
GetPath="select Value from Config where Name='ZM_PATH_SOCKS'"
dbHost=`echo $ZM_DB_HOST | cut -d: -f1`
dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2`
if [ "$dbPort" = "" ]
then
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
else
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
fi
[ ! -d $ZM_PATH_SOCK ] \
&& mkdir -m 774 $ZM_PATH_SOCK \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK
echo -n $"Starting $prog: "
$command start
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && touch $LOCKFILE
return $RETVAL
}
stop()
{
loadconf
echo -n $"Stopping $prog: "
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && rm -f $LOCKFILE
}
zmstatus()
{
loadconf
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
$ZM_PATH_BIN/zmu -l
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
}
zmupdate()
{
if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then
$ZM_PATH_BIN/zmupdate.pl -f
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'condrestart')
loadconf
result=`$ZM_PATH_BIN/zmdc.pl check`
if [ "$result" = "running" ]; then
$ZM_PATH_BIN/zmdc.pl shutdown > /dev/null
rm -f $LOCKFILE
start
fi
;;
'status')
status httpd
status mysqld
zmstatus
;;
*)
echo "Usage: $0 { start | stop | restart | condrestart | status }"
RETVAL=1
;;
esac
exit $RETVAL

View File

@ -1,7 +0,0 @@
@ZM_LOGDIR@/*log
{
weekly
notifempty
missingok
create 660 @WEB_USER@ @WEB_GROUP@
}

View File

@ -2,13 +2,13 @@
%global zmgid_final apache %global zmgid_final apache
# Crud is configured as a git submodule # Crud is configured as a git submodule
%global crud_version 3.0.10 %global crud_version 3.1.0-zm
# CakePHP-Enum-Behavior is configured as a git submodule
%global ceb_version 1.0-zm
%if "%{zmuid_final}" == "nginx" %if "%{zmuid_final}" == "nginx"
%global with_nginx 1 %global with_nginx 1
%global wwwconfdir %{_sysconfdir}/nginx/default.d
%else
%global wwwconfdir %{_sysconfdir}/httpd/conf.d
%endif %endif
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt %global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
@ -22,18 +22,11 @@
%global with_apcu_bc 1 %global with_apcu_bc 1
%endif %endif
# Include files for SysV init or systemd
%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7
%global with_init_systemd 1
%else
%global with_init_sysv 1
%endif
%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora} %global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.31.1 Version: 1.31.43
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons
@ -41,18 +34,18 @@ Group: System Environment/Daemons
# Mootools is inder the MIT license: http://mootools.net/ # Mootools is inder the MIT license: http://mootools.net/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp # CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# Crud is under the MIT license: https://github.com/FriendsOfCake/crud # Crud is under the MIT license: https://github.com/FriendsOfCake/crud
# CakePHP-Enum-Behavior is under the MIT license: https://github.com/asper/CakePHP-Enum-Behavior
License: GPLv2+ and LGPLv2+ and MIT License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/ URL: http://www.zoneminder.com/
Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz
Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source1: https://github.com/ZoneMinder/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz
%{?with_init_systemd:BuildRequires: systemd-devel} BuildRequires: systemd-devel
%{?with_init_systemd:BuildRequires: mariadb-devel} BuildRequires: mariadb-devel
%{?with_init_systemd:BuildRequires: perl-podlators} BuildRequires: perl-podlators
%{?with_init_systemd:BuildRequires: polkit-devel} BuildRequires: polkit-devel
%{?with_init_sysv:BuildRequires: mysql-devel}
%{?el6:BuildRequires: epel-rpm-macros}
BuildRequires: cmake >= 2.8.7 BuildRequires: cmake >= 2.8.7
BuildRequires: gnutls-devel BuildRequires: gnutls-devel
BuildRequires: bzip2-devel BuildRequires: bzip2-devel
@ -82,6 +75,7 @@ BuildRequires: vlc-devel
BuildRequires: libcurl-devel BuildRequires: libcurl-devel
BuildRequires: libv4l-devel BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel BuildRequires: ffmpeg-devel
BuildRequires: desktop-file-utils
# Required for mp4 container support # Required for mp4 container support
BuildRequires: libmp4v2-devel BuildRequires: libmp4v2-devel
@ -116,19 +110,10 @@ Requires: perl(LWP::Protocol::https)
Requires: ca-certificates Requires: ca-certificates
Requires: zip Requires: zip
%{?with_init_systemd:Requires(post): systemd} Requires(post): systemd
%{?with_init_systemd:Requires(post): systemd-sysv} Requires(post): systemd-sysv
%{?with_init_systemd:Requires(preun): systemd} Requires(preun): systemd
%{?with_init_systemd:Requires(postun): systemd} Requires(postun): systemd
%{?with_init_sysv:Requires(post): /sbin/chkconfig}
%{?with_init_sysv:Requires(post): %{_bindir}/checkmodule}
%{?with_init_sysv:Requires(post): %{_bindir}/semodule_package}
%{?with_init_sysv:Requires(post): %{_sbindir}/semodule}
%{?with_init_sysv:Requires(preun): /sbin/chkconfig}
%{?with_init_sysv:Requires(preun): /sbin/service}
%{?with_init_sysv:Requires(preun): %{_sbindir}/semodule}
%{?with_init_sysv:Requires(postun): /sbin/service}
Requires(post): %{_bindir}/gpasswd Requires(post): %{_bindir}/gpasswd
Requires(post): %{_bindir}/less Requires(post): %{_bindir}/less
@ -147,6 +132,11 @@ too much degradation of performance.
%{__rm} -rf ./web/api/app/Plugin/Crud %{__rm} -rf ./web/api/app/Plugin/Crud
%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud %{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud
# The all powerful autosetup macro does not work after the second source tarball
%{__gzip} -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf -
%{__rm} -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior
%{__mv} -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior
# Change the following default values # Change the following default values
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload ./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload
@ -167,6 +157,12 @@ too much degradation of performance.
%install %install
%make_install %make_install
desktop-file-install \
--dir %{buildroot}%{_datadir}/applications \
--delete-original \
--mode 644 \
%{buildroot}%{_datadir}/applications/zoneminder.desktop
# Remove unwanted files and folders # Remove unwanted files and folders
find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || : find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
@ -178,24 +174,10 @@ find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php
%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem %{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
%post %post
%if 0%{?with_init_sysv}
/sbin/chkconfig --add zoneminder
/sbin/chkconfig zoneminder on
# Create and load zoneminder selinux policy module
echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wait.\n"
%{_bindir}/checkmodule -M -m -o %{_docdir}/%{name}-%{version}/local_zoneminder.mod %{_docdir}/%{name}-%{version}/local_zoneminder.te > /dev/null 2>&1 || :
%{_bindir}/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null 2>&1 || :
%{_sbindir}/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null 2>&1 || :
%endif
%if 0%{?with_init_systemd}
# Initial installation # Initial installation
if [ $1 -eq 1 ] ; then if [ $1 -eq 1 ] ; then
%systemd_post %{name}.service %systemd_post %{name}.service
fi fi
%endif
# Upgrade from a previous version of zoneminder # Upgrade from a previous version of zoneminder
if [ $1 -eq 2 ] ; then if [ $1 -eq 2 ] ; then
@ -249,34 +231,11 @@ EOF
%endif %endif
%preun %preun
%if 0%{?with_init_sysv}
if [ $1 -eq 0 ]; then
/sbin/service zoneminder stop > /dev/null 2>&1 || :
/sbin/chkconfig --del zoneminder
echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n"
%{_sbindir}/semodule -r local_zoneminder.pp
fi
%endif
%if 0%{?with_init_systemd}
%systemd_preun %{name}.service %systemd_preun %{name}.service
%endif
%postun %postun
%if 0%{?with_init_sysv}
if [ $1 -ge 1 ]; then
/sbin/service zoneminder condrestart > /dev/null 2>&1 || :
fi
# Remove the doc folder.
rm -rf %{_docdir}/%{name}-%{version}
%endif
%if 0%{?with_init_systemd}
%systemd_postun_with_restart %{name}.service %systemd_postun_with_restart %{name}.service
%endif
%if 0%{?with_init_systemd}
%triggerun -- zoneminder < 1.25.0-4 %triggerun -- zoneminder < 1.25.0-4
# Save the current service runlevel info # Save the current service runlevel info
# User must manually run systemd-sysv-convert --apply zoneminder # User must manually run systemd-sysv-convert --apply zoneminder
@ -286,7 +245,6 @@ rm -rf %{_docdir}/%{name}-%{version}
# Run these because the SysV package being removed won't do them # Run these because the SysV package being removed won't do them
/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : /sbin/chkconfig --del zoneminder >/dev/null 2>&1 || :
/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || :
%endif
%files %files
%license COPYING %license COPYING
@ -304,25 +262,18 @@ rm -rf %{_docdir}/%{name}-%{version}
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf %config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf %ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf %config(noreplace) %attr(644,root,root) /etc/zm/www/zoneminder.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder %config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
%if 0%{?with_nginx} %if 0%{?with_nginx}
%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf %config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf
%endif %endif
%if 0%{?with_init_systemd}
%{_tmpfilesdir}/zoneminder.conf %{_tmpfilesdir}/zoneminder.conf
%{_unitdir}/zoneminder.service %{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy %{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%{_bindir}/zmsystemctl.pl %{_bindir}/zmsystemctl.pl
%endif
%if 0%{?with_init_sysv}
%doc distros/redhat/misc/local_zoneminder.te
%attr(755,root,root) %{_initrddir}/zoneminder
%endif
%{_bindir}/zma %{_bindir}/zma
%{_bindir}/zmaudit.pl %{_bindir}/zmaudit.pl
@ -341,6 +292,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_bindir}/zmtelemetry.pl %{_bindir}/zmtelemetry.pl
%{_bindir}/zmx10.pl %{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl %{_bindir}/zmonvif-probe.pl
%{_bindir}/zmstats.pl
%{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/ONVIF* %{perl_vendorlib}/ONVIF*
@ -351,6 +303,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_libexecdir}/zoneminder/ %{_libexecdir}/zoneminder/
%{_datadir}/zoneminder/ %{_datadir}/zoneminder/
%{_datadir}/applications/*%{name}.desktop
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events
@ -358,11 +311,18 @@ rm -rf %{_docdir}/%{name}-%{version}
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/cache/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog %changelog
* Sun Apr 22 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.31.42-1
- Remove support for sysvinit a.k.a. el6
- use desktop-file-install for new zoneminder.desktop file
- add new web cache folder
- 1.31.42 development snapshot
* Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1 * Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1
- modify autosetup macro parameters - modify autosetup macro parameters
- modify requirements for php-pecl-acpu-bc package - modify requirements for php-pecl-acpu-bc package

View File

@ -6,6 +6,12 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted Require all granted
</Directory> </Directory>
# Order matters. This Alias must come first
Alias /zm/cache /var/cache/zoneminder/cache
<Directory /var/cache/zoneminder/cache>
Options -Indexes +FollowSymLinks
</Directory>
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
php_flag register_globals off php_flag register_globals off
@ -15,6 +21,28 @@ Alias /zm /usr/share/zoneminder/www
</IfModule> </IfModule>
</Directory> </Directory>
<Directory /usr/share/zoneminder/www/api> # For better visibility, the following directives have been migrated from the
AllowOverride All # default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory> </Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>

View File

@ -54,7 +54,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,liburi-encode-perl ,liburi-encode-perl
,libwww-perl ,libwww-perl
,libdata-uuid-perl ,libdata-uuid-perl
,libnumber-bytes-human ,libnumber-bytes-human-perl
,libfile-slurp-perl ,libfile-slurp-perl
,mysql-client | virtual-mysql-client ,mysql-client | virtual-mysql-client
,perl-modules ,perl-modules

View File

@ -25,6 +25,7 @@ override_dh_auto_configure:
-DZM_SOCKDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \
-DZM_TMPDIR="/tmp/zm" \ -DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \ -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \

View File

@ -3,6 +3,7 @@ var/lib/zm
var/cache/zoneminder/events var/cache/zoneminder/events
var/cache/zoneminder/images var/cache/zoneminder/images
var/cache/zoneminder/temp var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db usr/share/zoneminder/db
etc/zm etc/zm
etc/zm/conf.d etc/zm/conf.d

View File

@ -6,6 +6,12 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted Require all granted
</Directory> </Directory>
# Order matters. This alias must come first.
Alias /zm/cache /var/cache/zoneminder/cache
<Directory /var/cache/zoneminder/cache>
Options -Indexes +FollowSymLinks
</Directory>
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks Options -Indexes +FollowSymLinks
@ -14,6 +20,27 @@ Alias /zm /usr/share/zoneminder/www
</IfModule> </IfModule>
</Directory> </Directory>
<Directory /usr/share/zoneminder/www/api> # For better visibility, the following directives have been migrated from the
AllowOverride All # default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory> </Directory>

View File

@ -59,7 +59,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libsoap-wsdl-perl ,libsoap-wsdl-perl
,libio-socket-multicast-perl ,libio-socket-multicast-perl
,libdigest-sha-perl ,libdigest-sha-perl
,libsys-cpu-perl, libsys-cpuload-perl, libsys-meminfo-perl ,libsys-cpu-perl, libsys-meminfo-perl
,libdata-uuid-perl ,libdata-uuid-perl
,libnumber-bytes-human-perl ,libnumber-bytes-human-perl
,libfile-slurp-perl ,libfile-slurp-perl

View File

@ -25,6 +25,7 @@ override_dh_auto_configure:
-DZM_SOCKDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \
-DZM_TMPDIR="/tmp/zm" \ -DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"

View File

@ -3,7 +3,7 @@ var/lib/zm
var/cache/zoneminder/events var/cache/zoneminder/events
var/cache/zoneminder/images var/cache/zoneminder/images
var/cache/zoneminder/temp var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db usr/share/zoneminder/db
usr/share/zoneminder/www/cache
etc/zm/ etc/zm/
etc/zm/conf.d etc/zm/conf.d

View File

@ -13,7 +13,7 @@ if [ "$1" = "configure" ]; then
chown www-data:root /var/log/zm chown www-data:root /var/log/zm
chown www-data:www-data /var/lib/zm chown www-data:www-data /var/lib/zm
if [ -z "$2" ]; then if [ -z "$2" ]; then
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* /usr/share/zoneminder/www/cache chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
fi fi
if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
@ -35,6 +35,7 @@ if [ "$1" = "configure" ]; then
else else
DBSERVICE="mysql.service" DBSERVICE="mysql.service"
fi fi
echo "Detected db service is $DBSERVICE"
if systemctl is-failed --quiet $DBSERVICE; then if systemctl is-failed --quiet $DBSERVICE; then
echo "$DBSERVICE is in a failed state; it will not be started." echo "$DBSERVICE is in a failed state; it will not be started."
echo "If you have already resolved the problem preventing $DBSERVICE from running," echo "If you have already resolved the problem preventing $DBSERVICE from running,"
@ -53,13 +54,20 @@ if [ "$1" = "configure" ]; then
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
# test if database if already present... # test if database if already present...
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
echo "Creating zm db"
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
# This creates the user. # This creates the user.
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else else
echo "Updating permissions"
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
else else
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi fi

View File

@ -1,4 +1,4 @@
d /var/run/zm 0755 www-data www-data d /var/run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data d /tmp/zm 0755 www-data www-data
d /var/tmp/zm 0755 www-data www-data d /var/tmp/zm 0755 www-data www-data
d /usr/share/zoneminder/www/cache 0755 www-data www-data d /var/cache/zoneminder/cache 0755 www-data www-data

View File

@ -45,8 +45,6 @@ The following notes are based on real problems which have occurred by those who
How to Install ZoneMinder How to Install ZoneMinder
------------------------- -------------------------
These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6.
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline: ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
:: ::
@ -57,13 +55,6 @@ Note that RHEL/CentOS 7 users should use yum instead of dnf.
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README. Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
How to Install ZoneMinder on RHEL/CentOS 6
------------------------------------------
We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near.
Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo <https://www.zoneminder.com>`_.
How to Install Nightly Development Builds How to Install Nightly Development Builds
----------------------------------------- -----------------------------------------

View File

@ -147,6 +147,23 @@ Keep aspect ratio
Orientation Orientation
As per local devices. As per local devices.
WebSite
^^^^^^^
This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
Website URL
Enter the full http or https url to the desired website.
Width (pixels)
Chose a desired width in pixels that gives an acceptable appearance. This may take some expirimentation.
Height (pixels)
Chose a desired height in pixels that gives an acceptable appearance. This may take some expirimentation.
Web Site Refresh
If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content.
Timestamp Tab Timestamp Tab
------------- -------------

View File

@ -8,6 +8,23 @@
ServerAdmin webmaster@localhost ServerAdmin webmaster@localhost
DocumentRoot "@WEB_PREFIX@" DocumentRoot "@WEB_PREFIX@"
# Order matters. This alias must come first.
Alias /zm/cache "@ZM_CACHEDIR@"
<Directory "@ZM_CACHEDIR@">
Options -Indexes +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
Alias /zm "@WEB_PREFIX@" Alias /zm "@WEB_PREFIX@"
<Directory "@WEB_PREFIX@"> <Directory "@WEB_PREFIX@">
Options -Indexes +FollowSymLinks Options -Indexes +FollowSymLinks
@ -38,6 +55,31 @@
</IfModule> </IfModule>
</Directory> </Directory>
# For better visibility, the following directives have been migrated from the
# default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "@ZM_WEBDIR@/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>
# Use the first option to have Apache logs written to the general log # Use the first option to have Apache logs written to the general log
# directory, or the second to have them written to the regular Apache # directory, or the second to have them written to the regular Apache
# directory (you may have to change the path to that used on your system) # directory (you may have to change the path to that used on your system)

View File

@ -5,4 +5,3 @@ Name=ZoneMinder
Comment= Comment=
Icon=@PKGDATADIR@/icons/16x16/icon.xpm Icon=@PKGDATADIR@/icons/16x16/icon.xpm
URL=http://localhost/zm/\r URL=http://localhost/zm/\r
Categories=GNOME;AudioVideo;Video;Recorder;

View File

@ -1723,26 +1723,6 @@ our @options = (
type => $types{abs_path}, type => $types{abs_path},
category => 'config', category => 'config',
}, },
{
name => 'ZM_SIGNAL_CHECK_POINTS',
default => '10',
description => 'How many points in a captured image to check for signal loss',
help => q`
For locally attached video cameras ZoneMinder can check for
signal loss by looking at a number of random points on each
captured image. If all of these points are set to the same
fixed colour then the camera is assumed to have lost signal.
When this happens any open events are closed and a short one
frame signal loss event is generated, as is another when the
signal returns. This option defines how many points on each
image to check. Note that this is a maximum, any points found
to not have the check colour will abort any further checks so
in most cases on a couple of points will actually be checked.
Network and file based cameras are never checked.
`,
type => $types{integer},
category => 'config',
},
{ {
name => 'ZM_V4L_MULTI_BUFFER', name => 'ZM_V4L_MULTI_BUFFER',
default => 'yes', default => 'yes',
@ -2966,6 +2946,23 @@ our @options = (
type => $types{boolean}, type => $types{boolean},
category => 'web', category => 'web',
}, },
{
name => 'ZM_WEB_XFRAME_WARN',
default => 'yes',
description => 'Warn when website X-Frame-Options is set to sameorigin',
help => q`
When creating a Web Site monitor, if the target web site has
X-Frame-Options set to sameorigin in the header, the site will
not display in ZoneMinder. This is a design feature in most modern
browsers. When this condiction has occured, ZoneMinder will write a
warning to the log file. To get around this, one can install a browser
plugin or extension to ignore X-Frame headers, and then the page will
display properly. Once the plugin or extenstion has ben installed,
the end user may choose to turn this warning off.
`,
type => $types{boolean},
category => 'web',
},
{ {
name => 'ZM_WEB_H_REFRESH_MAIN', name => 'ZM_WEB_H_REFRESH_MAIN',
default => '60', default => '60',

View File

@ -397,7 +397,7 @@ sub delete {
if ( (! $Config{ZM_OPT_FAST_DELETE}) and $event->Storage()->DoDelete() ) { if ( (! $Config{ZM_OPT_FAST_DELETE}) and $event->Storage()->DoDelete() ) {
$event->delete_files( ); $event->delete_files( );
} else { } else {
Debug('Not deleting frames, stats and files for speed.'); Debug('Not deleting event files from '.$event->Path().' for speed.');
} }
} # end sub delete } # end sub delete
@ -519,6 +519,22 @@ sub DiskSpace {
sub MoveTo { sub MoveTo {
my ( $self, $NewStorage ) = @_; my ( $self, $NewStorage ) = @_;
my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
if ( ! -e $OldPath ) {
return "Old path $OldPath does not exist.";
}
# First determine if we can move it to the dest.
# We do this before bothering to lock the event
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
return "New storage does not have an id. Moving will not happen.";
} elsif ( !$NewPath ) {
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {
return "New path $NewPath does not exist.";
}
$ZoneMinder::Database::dbh->begin_work(); $ZoneMinder::Database::dbh->begin_work();
$self->lock_and_load(); $self->lock_and_load();
# data is reloaded, so need to check that the move hasn't already happened. # data is reloaded, so need to check that the move hasn't already happened.
@ -526,25 +542,13 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
return "Event has already been moved by someone else."; return "Event has already been moved by someone else.";
} }
my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint if ( $$OldStorage{Id} != $$self{StorageId} ) {
$ZoneMinder::Database::dbh->commit();
return "Old Storage path changed, Event has moved somewhere else.";
}
$$self{Storage} = $NewStorage; $$self{Storage} = $NewStorage;
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
$ZoneMinder::Database::dbh->commit();
return "New storage does not have an id. Moving will not happen.";
} elsif ( !$NewPath ) {
$ZoneMinder::Database::dbh->commit();
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {
$ZoneMinder::Database::dbh->commit();
return "New path $NewPath does not exist.";
} elsif ( ! -e $OldPath ) {
$ZoneMinder::Database::dbh->commit();
return "Old path $OldPath does not exist.";
}
( $NewPath ) = ( $self->Path(undef) =~ /^(.*)$/ ); # De-taint ( $NewPath ) = ( $self->Path(undef) =~ /^(.*)$/ ); # De-taint
if ( $NewPath eq $OldPath ) { if ( $NewPath eq $OldPath ) {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();

View File

@ -160,9 +160,12 @@ sub Sql {
if ( $term->{attr} =~ /^Monitor/ ) { if ( $term->{attr} =~ /^Monitor/ ) {
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/; my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= 'M.'.$temp_attr_name; $self->{Sql} .= 'M.'.$temp_attr_name;
} elsif ( $term->{attr} =~ /^Server/ ) { } elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
$self->{Sql} .= 'M.'.$term->{attr};
} elsif ( $term->{attr} eq 'StorageServerId' ) {
$self->{Sql} .= 'S.'.$term->{attr}; $self->{Sql} .= 'S.'.$term->{attr};
} elsif ( $term->{attr} eq 'FilterServerId' ) {
$self->{Sql} .= $Config{ZM_SERVER_ID};
# StartTime options # StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) { } elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= 'E.StartTime'; $self->{Sql} .= 'E.StartTime';
@ -172,7 +175,7 @@ sub Sql {
$self->{Sql} .= 'to_days( E.StartTime )'; $self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'StartDate' ) { } elsif ( $term->{attr} eq 'StartDate' ) {
$self->{Sql} .= 'to_days( E.StartTime )'; $self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'Time' ) { } elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
$self->{Sql} .= 'extract( hour_second from E.StartTime )'; $self->{Sql} .= 'extract( hour_second from E.StartTime )';
} elsif ( $term->{attr} eq 'Weekday' ) { } elsif ( $term->{attr} eq 'Weekday' ) {
$self->{Sql} .= 'weekday( E.StartTime )'; $self->{Sql} .= 'weekday( E.StartTime )';
@ -208,7 +211,7 @@ sub Sql {
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $term->{attr} =~ /^MonitorName/ ) { if ( $term->{attr} =~ /^MonitorName/ ) {
$value = "'$temp_value'"; $value = "'$temp_value'";
} elsif ( $term->{attr} eq 'ServerId' ) { } elsif ( $term->{attr} =~ /ServerId/) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})"); Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
if ( $temp_value eq 'ZM_SERVER_ID' ) { if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'"; $value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
@ -241,7 +244,7 @@ sub Sql {
} }
$value = "'$value'"; $value = "'$value'";
} }
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) { } elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
if ( $temp_value eq 'NULL' ) { if ( $temp_value eq 'NULL' ) {
$value = $temp_value; $value = $temp_value;
} else { } else {

View File

@ -147,7 +147,7 @@ our $mem_data = {
last_write_index => { type=>'uint32', seq=>$mem_seq++ }, last_write_index => { type=>'uint32', seq=>$mem_seq++ },
last_read_index => { type=>'uint32', seq=>$mem_seq++ }, last_read_index => { type=>'uint32', seq=>$mem_seq++ },
state => { type=>'uint32', seq=>$mem_seq++ }, state => { type=>'uint32', seq=>$mem_seq++ },
last_event => { type=>'uint32', seq=>$mem_seq++ }, last_event => { type=>'uint64', seq=>$mem_seq++ },
action => { type=>'uint32', seq=>$mem_seq++ }, action => { type=>'uint32', seq=>$mem_seq++ },
brightness => { type=>'int32', seq=>$mem_seq++ }, brightness => { type=>'int32', seq=>$mem_seq++ },
hue => { type=>'int32', seq=>$mem_seq++ }, hue => { type=>'int32', seq=>$mem_seq++ },
@ -167,7 +167,6 @@ our $mem_data = {
last_read_time => { type=>'time_t64', seq=>$mem_seq++ }, last_read_time => { type=>'time_t64', seq=>$mem_seq++ },
control_state => { type=>'uint8[256]', seq=>$mem_seq++ }, control_state => { type=>'uint8[256]', seq=>$mem_seq++ },
alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ }, alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ },
} }
}, },
trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> { trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> {

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory. # will save memory.
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (
'functions' => [ qw( 'functions' => [ qw(
CpuLoad
) ] ) ]
); );
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -107,6 +108,17 @@ sub Hostname {
return $_[0]{Hostname}; return $_[0]{Hostname};
} # end sub Hostname } # end sub Hostname
sub CpuLoad {
my $output = qx(uptime);
my @sysloads = split ', ', (split ': ', $output)[-1];
if (join(', ',@sysloads) =~ /(\d+\.\d+)\s*,\s+(\d+\.\d+)\s*,\s+(\d+\.\d+)\s*$/) {
return @sysloads;
}
return (undef, undef, undef);
} # end sub CpuLoad
1; 1;
__END__ __END__
# Below is stub documentation for your module. You'd better edit it! # Below is stub documentation for your module. You'd better edit it!

View File

@ -113,6 +113,15 @@ sub Name {
return $_[0]{Name}; return $_[0]{Name};
} # end sub Path } # end sub Path
sub DoDelete {
my $self = shift;
$$self{DoDelete} = shift if @_;
if ( ! defined $$self{DoDelete} ) {
$$self{DoDelete} = 1;
}
return $$self{DoDelete};
}
sub Server { sub Server {
my $self = shift; my $self = shift;
if ( ! $$self{Server} ) { if ( ! $$self{Server} ) {

View File

@ -63,6 +63,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $report = 0; my $report = 0;
my $interactive = 0; my $interactive = 0;
my $continuous = 0; my $continuous = 0;
my $level = 1;
my $monitor_id = 0; my $monitor_id = 0;
my $version; my $version;
my $force = 0; my $force = 0;
@ -74,6 +75,7 @@ GetOptions(
continuous =>\$continuous, continuous =>\$continuous,
force =>\$force, force =>\$force,
interactive =>\$interactive, interactive =>\$interactive,
level =>\$level,
'monitor_id=i' =>\$monitor_id, 'monitor_id=i' =>\$monitor_id,
report =>\$report, report =>\$report,
'storage_id=i' =>\$storage_id, 'storage_id=i' =>\$storage_id,
@ -485,7 +487,9 @@ MAIN: while( $loop ) {
redo MAIN; redo MAIN;
} }
if ( $level > 1 ) {
# Remove orphaned events (with no monitor) # Remove orphaned events (with no monitor)
# Shouldn't be possible anymore with FOREIGN KEYS in place
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Events"); Debug("Checking for Orphaned Events");
my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name
@ -507,6 +511,7 @@ MAIN: while( $loop ) {
} }
} }
redo MAIN if $cleaned; redo MAIN if $cleaned;
} # end if level > 1
# Remove empty events (with no frames) # Remove empty events (with no frames)
$cleaned = 0; $cleaned = 0;
@ -537,7 +542,7 @@ MAIN: while( $loop ) {
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Frames"); Debug("Checking for Orphaned Frames");
my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)'; WHERE (SELECT COUNT(*) FROM Events WHERE Events.Id=EventId)=0';
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() ); or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute() $res = $selectOrphanedFramesSth->execute()
@ -552,6 +557,7 @@ MAIN: while( $loop ) {
} }
redo MAIN if $cleaned; redo MAIN if $cleaned;
if ( $level > 1 ) {
# Remove orphaned stats records # Remove orphaned stats records
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Stats"); Debug("Checking for Orphaned Stats");
@ -570,6 +576,7 @@ MAIN: while( $loop ) {
} }
} }
redo MAIN if ( $cleaned ); redo MAIN if ( $cleaned );
}
# New audit to close any events that were left open for longer than MIN_AGE seconds # New audit to close any events that were left open for longer than MIN_AGE seconds
my $selectUnclosedEventsSql = my $selectUnclosedEventsSql =

View File

@ -104,23 +104,23 @@ my @daemons = (
'zmtelemetry.pl' 'zmtelemetry.pl'
); );
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION}) { if ( $Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
push @daemons,'zmeventnotification.pl'; push @daemons,'zmeventnotification.pl';
} }
my $command = shift @ARGV; my $command = shift @ARGV;
if( ! $command ) { if ( !$command ) {
print( STDERR "No command given\n" ); print(STDERR "No command given\n");
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
if ( $command eq 'version' ) { if ( $command eq 'version' ) {
print ZoneMinder::Base::ZM_VERSION."\n"; print ZoneMinder::Base::ZM_VERSION."\n";
exit( 0 ); exit(0);
} }
my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/; my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/;
my $daemon = shift( @ARGV ); my $daemon = shift @ARGV;
if ( $needs_daemon && ! $daemon ) { if ( $needs_daemon && ! $daemon ) {
print( STDERR "No daemon given\n" ); print(STDERR "No daemon given\n");
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
my @args; my @args;
@ -130,7 +130,7 @@ if ( $needs_daemon ) {
if ( $daemon =~ /^${daemon_patt}$/ ) { if ( $daemon =~ /^${daemon_patt}$/ ) {
$daemon = $1; $daemon = $1;
} else { } else {
print( STDERR "Invalid daemon '$daemon' specified" ); print(STDERR "Invalid daemon '$daemon' specified");
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
} }
@ -141,22 +141,22 @@ foreach my $arg ( @ARGV ) {
if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) { if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) {
push( @args, $1 ); push( @args, $1 );
} else { } else {
print( STDERR "Bogus argument '$arg' found" ); print(STDERR "Bogus argument '$arg' found");
exit( -1 ); exit(-1);
} }
} }
my $dbh = zmDbConnect(); my $dbh = zmDbConnect();
socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
my $saddr = sockaddr_un( SOCK_FILE ); my $saddr = sockaddr_un(SOCK_FILE);
my $server_up = connect( CLIENT, $saddr ); my $server_up = connect(CLIENT, $saddr);
if ( ! $server_up ) { if ( !$server_up ) {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad; use ZoneMinder::Server qw(CpuLoad);
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { 'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr()); Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr());
@ -168,16 +168,16 @@ use Sys::CpuLoad;
exit(); exit();
} }
if ( $command eq 'check' ) { if ( $command eq 'check' ) {
print( "stopped\n" ); print("stopped\n");
exit(); exit();
} elsif ( $command ne 'startup' ) { } elsif ( $command ne 'startup' ) {
print( "Unable to connect to server using socket at " . SOCK_FILE . "\n" ); print("Unable to connect to server using socket at " . SOCK_FILE . "\n");
exit( -1 ); exit( -1 );
} }
# The server isn't there # The server isn't there
print( "Starting server\n" ); print("Starting server\n");
close( CLIENT ); close(CLIENT);
if ( my $cpid = fork() ) { if ( my $cpid = fork() ) {
# Parent process just sleep and fall through # Parent process just sleep and fall through
@ -185,7 +185,7 @@ use Sys::CpuLoad;
# I'm still not sure why we need to re-init the logs # I'm still not sure why we need to re-init the logs
logInit(); logInit();
socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
my $attempts = 0; my $attempts = 0;
while( !connect(CLIENT, $saddr) ) { while( !connect(CLIENT, $saddr) ) {
$attempts++; $attempts++;
@ -209,7 +209,6 @@ if ( ($command eq 'check') && !$daemon ) {
} }
# The server is there, connect to it # The server is there, connect to it
#print( "Writing commands\n" );
CLIENT->autoflush(); CLIENT->autoflush();
my $message = join(';', $command, ( $daemon ? $daemon : () ), @args ); my $message = join(';', $command, ( $daemon ? $daemon : () ), @args );
print(CLIENT $message); print(CLIENT $message);
@ -218,9 +217,7 @@ while( my $line = <CLIENT> ) {
chomp($line); chomp($line);
print("$line\n"); print("$line\n");
} }
# And we're done!
close(CLIENT); close(CLIENT);
#print( "Finished writing, bye\n" );
exit; exit;
@ -237,45 +234,46 @@ use Socket;
use IO::Handle; use IO::Handle;
use Time::HiRes qw(usleep); use Time::HiRes qw(usleep);
use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad; use ZoneMinder::Server qw(CpuLoad);
#use Data::Dumper; #use Data::Dumper;
# We count 10 of these, so total timeout is this value *10. use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sending KILL
use constant KILL_DELAY => 1; # seconds
our %cmd_hash; our %cmd_hash;
our %pid_hash; our %pid_hash;
our %terminating_processes;
sub run { sub run {
my $fd = 0; my $fd = 0;
while( $fd < POSIX::sysconf( &POSIX::_SC_OPEN_MAX ) ) { while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close( $fd++ ); POSIX::close($fd++);
} }
setpgrp(); setpgrp();
logInit(); logInit();
dPrint( ZoneMinder::Logger::INFO, 'Server starting at ' dPrint(ZoneMinder::Logger::INFO, 'Server starting at '
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime('%y/%m/%d %H:%M:%S', localtime())
."\n" ."\n"
); );
if ( open( my $PID, '>', ZM_PID ) ) { if ( open(my $PID, '>', ZM_PID) ) {
print( $PID $$ ); print($PID $$);
close( $PID ); close($PID);
} else { } else {
Error( "Can't open pid file at " . ZM_PID ); Error("Can't open pid file at " . ZM_PID);
} }
killAll( 1 ); # Tell any existing processes to die, wait 1 second between TERM and KILL
killAll(1);
dPrint( ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE ); dPrint(ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE);
my $dbh = zmDbConnect(1); my $dbh = zmDbConnect(1);
socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
unlink( main::SOCK_FILE ) or Error( 'Unable to unlink ' . main::SOCK_FILE .". Error message was: $!" ) if -e main::SOCK_FILE; unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if -e main::SOCK_FILE;
bind( SERVER, $saddr ) or Fatal( "Can't bind to " . main::SOCK_FILE . ": $!" ); bind(SERVER, $saddr) or Fatal("Can't bind to " . main::SOCK_FILE . ": $!");
listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
$SIG{CHLD} = \&reaper; $SIG{CHLD} = \&reaper;
$SIG{INT} = \&shutdownAll; $SIG{INT} = \&shutdownAll;
@ -284,7 +282,7 @@ sub run {
$SIG{HUP} = \&logrot; $SIG{HUP} = \&logrot;
my $rin = ''; my $rin = '';
vec( $rin, fileno(SERVER), 1 ) = 1; vec($rin, fileno(SERVER), 1) = 1;
my $win = $rin; my $win = $rin;
my $ein = $win; my $ein = $win;
my $timeout = 1; my $timeout = 1;
@ -293,8 +291,8 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
require ZoneMinder::Server; require ZoneMinder::Server;
$Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} ); $Server = new ZoneMinder::Server($Config{ZM_SERVER_ID});
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name} ); dPrint(ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name});
} }
while( 1 ) { while( 1 ) {
@ -302,8 +300,7 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
if ( ! ( $secs_count % 60 ) ) { if ( ! ( $secs_count % 60 ) ) {
$dbh = zmDbConnect() if ! $dbh->ping(); $dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load(); my @cpuload = CpuLoad();
dPrint( ZoneMinder::Logger::DEBUG, 'Updating Server record' );
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { 'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
@ -311,52 +308,51 @@ sub run {
} }
$secs_count += 1; $secs_count += 1;
} }
my $nfound = select( my $rout = $rin, undef, undef, $timeout ); my $nfound = select(my $rout = $rin, undef, undef, $timeout);
if ( $nfound > 0 ) { if ( $nfound > 0 ) {
if ( vec( $rout, fileno(SERVER), 1 ) ) { if ( vec($rout, fileno(SERVER), 1) ) {
my $paddr = accept( CLIENT, SERVER ); my $paddr = accept(CLIENT, SERVER);
my $message = <CLIENT>; my $message = <CLIENT>;
next if !$message; next if !$message;
my ( $command, $daemon, @args ) = split( /;/, $message ); my ( $command, $daemon, @args ) = split(';', $message);
if ( $command eq 'start' ) { if ( $command eq 'start' ) {
start( $daemon, @args ); start($daemon, @args);
} elsif ( $command eq 'stop' ) { } elsif ( $command eq 'stop' ) {
stop( $daemon, @args ); stop($daemon, @args);
} elsif ( $command eq 'restart' ) { } elsif ( $command eq 'restart' ) {
restart( $daemon, @args ); restart($daemon, @args);
} elsif ( $command eq 'reload' ) { } elsif ( $command eq 'reload' ) {
reload( $daemon, @args ); reload($daemon, @args);
} elsif ( $command eq 'startup' ) { } elsif ( $command eq 'startup' ) {
# Do nothing, this is all we're here for # Do nothing, this is all we're here for
dPrint( ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n" ); dPrint(ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n");
} elsif ( $command eq 'shutdown' ) { } elsif ( $command eq 'shutdown' ) {
shutdownAll(); shutdownAll();
} elsif ( $command eq 'check' ) { } elsif ( $command eq 'check' ) {
check( $daemon, @args ); check($daemon, @args);
} elsif ( $command eq 'status' ) { } elsif ( $command eq 'status' ) {
if ( $daemon ) { if ( $daemon ) {
status( $daemon, @args ); status($daemon, @args);
} else { } else {
status(); status();
} }
} elsif ( $command eq 'logrot' ) { } elsif ( $command eq 'logrot' ) {
logrot(); logrot();
} else { } else {
dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
} }
close(CLIENT); close(CLIENT);
} else { } else {
Fatal('Bogus descriptor'); Error('Bogus descriptor');
} }
} elsif ( $nfound < 0 ) { } elsif ( $nfound < 0 ) {
if ( $! == EINTR ) { if ( $! == EINTR ) {
# Dead child, will be reaped # Dead child, will be reaped
#print( "Probable dead child\n" ); #print( "Probable dead child\n" );
# See if it needs to start up again # See if it needs to start up again
restartPending();
} elsif ( $! == EPIPE ) { } elsif ( $! == EPIPE ) {
Error("Can't select: $!"); Error("Can't select: $!");
} else { } else {
@ -364,21 +360,25 @@ sub run {
} }
} else { } else {
#print( "Select timed out\n" ); #print( "Select timed out\n" );
restartPending();
} }
}
dPrint( ZoneMinder::Logger::INFO, 'Server exiting at ' restartPending();
check_for_processes_to_kill();
} # end while
dPrint(ZoneMinder::Logger::INFO, 'Server exiting at '
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
$dbh = zmDbConnect() if ! $dbh->ping(); $dbh = zmDbConnect() if ! $dbh->ping();
if ( ! defined $dbh->do(q{UPDATE Servers SET Status='NotRunning' WHERE Id=?}, undef, $Config{ZM_SERVER_ID} ) ) { if ( ! defined $dbh->do(q{UPDATE Servers SET Status='NotRunning' WHERE Id=?}, undef, $Config{ZM_SERVER_ID}) ) {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
} }
} }
unlink( main::SOCK_FILE ) or Error( 'Unable to unlink ' . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if ( -e main::SOCK_FILE );
unlink( ZM_PID ) or Error( 'Unable to unlink ' . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID ); unlink(ZM_PID) or Error('Unable to unlink ' . ZM_PID .". Error message was: $!") if ( -e ZM_PID );
exit(); exit();
} }
@ -395,15 +395,15 @@ sub dPrint {
print CLIENT @_ print CLIENT @_
} }
if ( $logLevel == ZoneMinder::Logger::DEBUG ) { if ( $logLevel == ZoneMinder::Logger::DEBUG ) {
Debug( @_ ); Debug(@_);
} elsif ( $logLevel == ZoneMinder::Logger::INFO ) { } elsif ( $logLevel == ZoneMinder::Logger::INFO ) {
Info( @_ ); Info(@_);
} elsif ( $logLevel == ZoneMinder::Logger::WARNING ) { } elsif ( $logLevel == ZoneMinder::Logger::WARNING ) {
Warning( @_ ); Warning(@_);
} elsif ( $logLevel == ZoneMinder::Logger::ERROR ) { } elsif ( $logLevel == ZoneMinder::Logger::ERROR ) {
Error( @_ ); Error(@_);
} elsif ( $logLevel == ZoneMinder::Logger::FATAL ) { } elsif ( $logLevel == ZoneMinder::Logger::FATAL ) {
Fatal( @_ ); Fatal(@_);
} }
} }
@ -411,61 +411,60 @@ sub start {
my $daemon = shift; my $daemon = shift;
my @args = @_; my @args = @_;
my $command = join(' ', $daemon, @args ); my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( !$process ) { if ( !$process ) {
# It's not running, or at least it's not been started by us # It's not running, or at least it's not been started by us
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef }; $process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) { } elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at " dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}\n" .", pid = $process->{pid}\n"
); );
return(); return;
} }
my $sigset = POSIX::SigSet->new; my $sigset = POSIX::SigSet->new;
my $blockset = POSIX::SigSet->new( SIGCHLD ); my $blockset = POSIX::SigSet->new( SIGCHLD );
sigprocmask( SIG_BLOCK, $blockset, $sigset ) or Fatal( "Can't block SIGCHLD: $!" ); sigprocmask(SIG_BLOCK, $blockset, $sigset) or Fatal("Can't block SIGCHLD: $!");
if ( my $cpid = fork() ) { if ( my $cpid = fork() ) {
logReinit(); logReinit();
$process->{pid} = $cpid; $process->{pid} = $cpid;
$process->{started} = time(); $process->{started} = time();
delete( $process->{pending} ); delete $process->{pending};
dPrint( ZoneMinder::Logger::INFO, "'$command' starting at " dPrint(ZoneMinder::Logger::INFO, "'$command' starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}\n" .", pid = $process->{pid}\n"
); );
$cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process; $cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process;
sigprocmask( SIG_SETMASK, $sigset ) or Fatal( "Can't restore SIGCHLD: $!" ); sigprocmask(SIG_SETMASK, $sigset) or Fatal("Can't restore SIGCHLD: $!");
} elsif ( defined($cpid ) ) { } elsif ( defined($cpid) ) {
# Force reconnection to the db. # Force reconnection to the db.
$dbh = zmDbConnect(1); $dbh = zmDbConnect(1);
logReinit(); logReinit();
dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) ) dPrint(ZoneMinder::Logger::INFO, "'$command' started at "
."' started at " .strftime('%y/%m/%d %H:%M:%S', localtime())
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
if ( $daemon =~ /^${daemon_patt}$/ ) { if ( $daemon =~ /^${daemon_patt}$/ ) {
$daemon = $Config{ZM_PATH_BIN}.'/'.$1; $daemon = $Config{ZM_PATH_BIN}.'/'.$1;
} else { } else {
Fatal( "Invalid daemon '$daemon' specified" ); Fatal("Invalid daemon '$daemon' specified");
} }
my @good_args; my @good_args;
foreach my $arg ( @args ) { foreach my $arg ( @args ) {
# Detaint arguments, if they look ok # Detaint arguments, if they look ok
if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) { if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) {
push( @good_args, $1 ); push @good_args, $1;
} else { } else {
Fatal( "Bogus argument '$arg' found" ); Fatal("Bogus argument '$arg' found");
} }
} }
@ -473,8 +472,8 @@ sub start {
zmDbDisconnect(); zmDbDisconnect();
my $fd = 0; my $fd = 0;
while( $fd < POSIX::sysconf( &POSIX::_SC_OPEN_MAX ) ) { while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close( $fd++ ); POSIX::close($fd++);
} }
# Child process # Child process
@ -483,11 +482,11 @@ sub start {
$SIG{TERM} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT';
$SIG{ABRT} = 'DEFAULT'; $SIG{ABRT} = 'DEFAULT';
exec( $daemon, @good_args ) or Fatal( "Can't exec: $!" ); exec($daemon, @good_args) or Fatal("Can't exec: $!");
} else { } else {
Fatal( "Can't fork: $!" ); Fatal("Can't fork: $!");
} }
} } # end sub start
# Sends the stop signal, without waiting around to see if the process died. # Sends the stop signal, without waiting around to see if the process died.
sub send_stop { sub send_stop {
@ -496,9 +495,9 @@ sub send_stop {
my $command = $process->{command}; my $command = $process->{command};
if ( $process->{pending} ) { if ( $process->{pending} ) {
delete( $cmd_hash{$command} ); delete $cmd_hash{$command};
dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at " dPrint(ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime('%y/%m/%d %H:%M:%S', localtime())
."\n" ."\n"
); );
return(); return();
@ -506,130 +505,114 @@ sub send_stop {
my $pid = $process->{pid}; my $pid = $process->{pid};
if ( !$pid_hash{$pid} ) { if ( !$pid_hash{$pid} ) {
dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' pid $pid is running\n" ); dPrint(ZoneMinder::Logger::ERROR, "No process with command of '$command' pid $pid is running\n");
return(); return();
} }
dPrint( ZoneMinder::Logger::INFO, "'$command' sending stop to pid $pid at " dPrint(ZoneMinder::Logger::INFO, "'$command' sending stop to pid $pid at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime('%y/%m/%d %H:%M:%S', localtime())
."\n" ."\n"
); );
$process->{keepalive} = !$final; $process->{keepalive} = !$final;
kill( 'TERM', $pid ); $process->{term_sent_at} = time;
$process->{pending} = 0;
$terminating_processes{$command} = $process;
kill('TERM', $pid);
return $pid; return $pid;
} # end sub send_stop } # end sub send_stop
sub kill_until_dead { sub check_for_processes_to_kill {
my ( $process ) = @_; # Turn off SIGCHLD
# Now check it has actually gone away, if not kill -9 it
my $count = 0;
my $sigset = POSIX::SigSet->new; my $sigset = POSIX::SigSet->new;
my $blockset = POSIX::SigSet->new(SIGCHLD); my $blockset = POSIX::SigSet->new(SIGCHLD);
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n"; sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
while( $process and $$process{pid} and kill( 0, $$process{pid} ) ) { foreach my $command ( %terminating_processes ) {
if ( $count++ > 10 ) { my $process = $cmd_hash{$command};
dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " Debug("Have process $command at pid $$process{pid} $$process{term_sent_at}");
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) if ( $$process{term_sent_at} and ( $$process{term_sent_at} - time > KILL_DELAY ) ) {
.". Sending KILL to pid $$process{pid}\n" dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
); .strftime('%y/%m/%d %H:%M:%S', localtime())
kill( 'KILL', $$process{pid} ); .' after ' . KILL_DELAY . ' seconds.'
last; ." Sending KILL to pid $$process{pid}\n"
);
kill('KILL', $$process{pid});
} }
# THe purpose of the signal blocking is to simplify the concurrency
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
sleep( KILL_DELAY );
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n";
} }
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
} } # end sub check_for_processess_to_kill
sub _stop {
my ($final, $process ) = @_;
my $pid = send_stop( $final, $process );
return if ! $pid;
delete( $cmd_hash{$$process{command}} );
kill_until_dead( $process );
}
sub stop { sub stop {
my ( $daemon, @args ) = @_; my ( $daemon, @args ) = @_;
my $command = join(' ', $daemon, @args ); my $command = join(' ', $daemon, @args );
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( !$process ) { if ( !$process ) {
dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" ); dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n");
return(); return;
} }
_stop( 1, $process ); send_stop(1, $process);
} }
# restart is the same as stop, except that we flag the processes for restarting once it dies
# One difference is that if we don't know about the process, then we start it.
sub restart { sub restart {
my $daemon = shift; my ( $daemon, @args ) = @_;
my @args = @_;
my $command = $daemon; my $command = join(' ', $daemon, @args);
$command .= ' '.join( ' ', ( @args ) ) if @args; dPrint(ZoneMinder::Logger::DEBUG, "Restarting $command\n");
dPrint ( ZoneMinder::Logger::DEBUG, "Restarting $command\n");
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( $process ) { if ( !$process ) {
dPrint( ZoneMinder::Logger::DEBUG, "Have process" ); dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n");
if ( $process->{pid} ) { start($daemon, @args);
dPrint( ZoneMinder::Logger::DEBUG, "Have process pid " .$process->{pid} ); return;
my $cpid = $process->{pid};
if ( defined($pid_hash{$cpid}) ) {
dPrint( ZoneMinder::Logger::DEBUG, "Have process pid hash " .$process->{pid} );
_stop( 0, $process );
return;
} else {
dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" );
}
}
} }
start( $daemon, @args ); # Start will be handled by the reaper
send_stop(0, $process);
return;
} }
sub reload { sub reload {
my $daemon = shift; my $daemon = shift;
my @args = @_; my @args = @_;
my $command = $daemon; my $command = join(' ', $daemon, @args);
$command .= ' '.join( ' ', ( @args ) ) if ( @args );
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( $process ) { if ( $process ) {
if ( $process->{pid} ) { if ( $process->{pid} ) {
kill( 'HUP', $process->{pid} ); kill('HUP', $process->{pid});
} }
} }
} }
sub logrot { sub logrot {
logReinit(); logReinit();
foreach my $process ( values( %pid_hash ) ) { foreach my $process ( values %pid_hash ) {
if ( $process->{pid} ) { if ( $process->{pid} ) {
# && $process->{command} =~ /^zm.*\.pl/ ) { # && $process->{command} =~ /^zm.*\.pl/ ) {
kill( 'HUP', $process->{pid} ); kill('HUP', $process->{pid});
} }
} }
} }
sub reaper { sub reaper {
my $saved_status = $!; my $saved_status = $!;
while ( (my $cpid = waitpid( -1, WNOHANG )) > 0 ) { while ( (my $cpid = waitpid(-1, WNOHANG)) > 0 ) {
my $status = $?; my $status = $?;
my $process = $pid_hash{$cpid}; my $process = $pid_hash{$cpid};
delete( $pid_hash{$cpid} ); delete $pid_hash{$cpid};
if ( !$process ) { if ( !$process ) {
dPrint( ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n" ); dPrint(ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n");
next; next;
} }
delete $terminating_processes{$$process{command}};
delete $$process{term_sent_at};
$process->{stopped} = time(); $process->{stopped} = time();
$process->{runtime} = ($process->{stopped}-$process->{started}); $process->{runtime} = ($process->{stopped}-$process->{started});
delete( $process->{pid} ); delete $process->{pid};
my $exit_status = $status>>8; my $exit_status = $status>>8;
my $exit_signal = $status&0xfe; my $exit_signal = $status&0xfe;
@ -656,9 +639,9 @@ sub reaper {
$out_str .= "\n"; $out_str .= "\n";
if ( $exit_status == 0 ) { if ( $exit_status == 0 ) {
Info( $out_str ); Info($out_str);
} else { } else {
Error( $out_str ); Error($out_str);
} }
if ( $process->{keepalive} ) { if ( $process->{keepalive} ) {
@ -676,6 +659,8 @@ sub reaper {
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY}; $process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
} }
} }
} else {
delete $cmd_hash{$$process{command}};
} }
} }
$SIG{CHLD} = \&reaper; $SIG{CHLD} = \&reaper;
@ -686,8 +671,8 @@ sub restartPending {
# Restart any pending processes # Restart any pending processes
foreach my $process ( values( %cmd_hash ) ) { foreach my $process ( values( %cmd_hash ) ) {
if ( $process->{pending} && $process->{pending} <= time() ) { if ( $process->{pending} && $process->{pending} <= time() ) {
dPrint( ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n" ); dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n");
start( $process->{daemon}, @{$process->{args}} ); start($process->{daemon}, @{$process->{args}});
} }
} }
} }
@ -696,29 +681,20 @@ sub shutdownAll {
foreach my $pid ( keys %pid_hash ) { foreach my $pid ( keys %pid_hash ) {
# This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here. # This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here.
next if ! $pid_hash{$pid}; next if ! $pid_hash{$pid};
send_stop( 1, $pid_hash{$pid} ); send_stop(1, $pid_hash{$pid});
} }
foreach my $pid ( keys %pid_hash ) { while ( %terminating_processes ) {
# This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here. check_for_processes_to_kill();
next if ! $pid_hash{$pid}; sleep(1) if %terminating_processes;
my $process = $pid_hash{$pid};
kill_until_dead( $process );
delete( $cmd_hash{$$process{command}} );
delete( $pid_hash{$pid} );
} }
if ( 0 ) { dPrint(ZoneMinder::Logger::INFO, "Server shutdown at "
killAll( 5 ); .strftime('%y/%m/%d %H:%M:%S', localtime())
}
dPrint( ZoneMinder::Logger::INFO, "Server shutdown at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); unlink(main::SOCK_FILE) or Error("Unable to unlink " . main::SOCK_FILE .". Error message was: $!") if ( -e main::SOCK_FILE );
unlink( ZM_PID ) or Error( "Unable to unlink " . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID ); unlink(ZM_PID) or Error("Unable to unlink " . ZM_PID .". Error message was: $!") if ( -e ZM_PID );
close( CLIENT ); close(CLIENT);
close( SERVER ); close(SERVER);
exit(); exit();
} }
@ -726,18 +702,18 @@ sub check {
my $daemon = shift; my $daemon = shift;
my @args = @_; my @args = @_;
my $command = join( ' ', $daemon, @args ); my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( !$process ) { if ( !$process ) {
cPrint( "unknown\n" ); cPrint("unknown\n");
} elsif ( $process->{pending} ) { } elsif ( $process->{pending} ) {
cPrint( "pending\n" ); cPrint("pending\n");
} else { } else {
my $cpid = $process->{pid}; my $cpid = $process->{pid};
if ( ! $pid_hash{$cpid} ) { if ( ! $pid_hash{$cpid} ) {
cPrint( "stopped\n" ); cPrint("stopped\n");
} else { } else {
cPrint( "running\n" ); cPrint("running\n");
} }
} }
} }
@ -747,53 +723,54 @@ sub status {
my @args = @_; my @args = @_;
if ( defined($daemon) ) { if ( defined($daemon) ) {
my $command = join( ' ', $daemon, @args ); my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( ! $process ) { if ( ! $process ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
return(); return;
} }
if ( $process->{pending} ) { if ( $process->{pending} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " dPrint(ZoneMinder::Logger::DEBUG, "'$command' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending}))
."\n" ."\n"
); );
} else { } else {
my $cpid = $process->{pid}; my $pid = $process->{pid};
if ( ! $pid_hash{$cpid} ) { if ( ! $pid_hash{$pid} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
return(); return;
} }
} }
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since " dPrint(ZoneMinder::Logger::DEBUG, "'$command' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started} ) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}" .", pid = $process->{pid}"
); );
} else { } else {
foreach my $process ( values(%pid_hash) ) { foreach my $process ( values %pid_hash ) {
my $out_str = "'$process->{command}' running since " my $out_str = "'$process->{command}' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}" .", pid = $process->{pid}"
; ;
$out_str .= ", valid" if ( kill( 0, $process->{pid} ) ); $out_str .= ", valid" if ( kill(0, $process->{pid}) );
$out_str .= "\n"; $out_str .= "\n";
dPrint( ZoneMinder::Logger::DEBUG, $out_str ); dPrint(ZoneMinder::Logger::DEBUG, $out_str);
} }
foreach my $process ( values( %cmd_hash ) ) { foreach my $process ( values %cmd_hash ) {
if ( $process->{pending} ) { if ( $process->{pending} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " dPrint(ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) .strftime( '%y/%m/%d %H:%M:%S', localtime($process->{pending}))
."\n" ."\n"
); );
} }
} # end foreach process } # end foreach process
} }
} } # end sub status
sub killAll { sub killAll {
my $delay = shift; my $delay = shift;
sleep( $delay ); # Why sleep before sending term?
#sleep( $delay );
my $killall; my $killall;
if ( '@HOST_OS@' eq 'BSD' ) { if ( '@HOST_OS@' eq 'BSD' ) {
$killall = 'killall -q -'; $killall = 'killall -q -';
@ -804,16 +781,15 @@ sub killAll {
} }
foreach my $daemon ( @daemons ) { foreach my $daemon ( @daemons ) {
my $cmd = $killall ."TERM $daemon"; my $cmd = $killall ."TERM $daemon";
Debug( $cmd ); Debug($cmd);
qx( $cmd ); qx($cmd);
} }
sleep( $delay ); sleep($delay);
foreach my $daemon ( @daemons ) { foreach my $daemon ( @daemons ) {
my $cmd = $killall."KILL $daemon"; my $cmd = $killall."KILL $daemon";
Debug( $cmd ); Debug($cmd);
qx( $cmd ); qx($cmd);
} }
} }
1; 1;
__END__ __END__

View File

@ -283,6 +283,8 @@ sub checkFilter {
foreach my $event ( @Events ) { foreach my $event ( @Events ) {
last if $zm_terminate; last if $zm_terminate;
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
Debug( "Checking event $event->{Id}" ); Debug( "Checking event $event->{Id}" );
my $delete_ok = !undef; my $delete_ok = !undef;
$dbh->ping(); $dbh->ping();
@ -302,7 +304,7 @@ sub checkFilter {
} }
if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) { if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) {
if ( !$event->{Emailed} ) { if ( !$event->{Emailed} ) {
$delete_ok = undef if ( !sendEmail( $filter, $event ) ); $delete_ok = undef if ( !sendEmail( $filter, $Event ) );
} }
} }
if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) { if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) {
@ -322,7 +324,6 @@ sub checkFilter {
} }
if ( $filter->{AutoDelete} ) { if ( $filter->{AutoDelete} ) {
if ( $delete_ok ) { if ( $delete_ok ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$Event->delete(); $Event->delete();
} else { } else {
Error( "Unable toto delete event $event->{Id} as previous operations failed\n" ); Error( "Unable toto delete event $event->{Id} as previous operations failed\n" );
@ -330,19 +331,23 @@ sub checkFilter {
} # end if AutoDelete } # end if AutoDelete
if ( $filter->{AutoMove} ) { if ( $filter->{AutoMove} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} ); my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );
$_ = $Event->MoveTo( $NewStorage ); $_ = $Event->MoveTo( $NewStorage );
Error($_) if $_; Error($_) if $_;
} }
if ( $filter->{UpdateDiskSpace} ) { if ( $filter->{UpdateDiskSpace} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$ZoneMinder::Database::dbh->begin_work(); $ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load(); $Event->lock_and_load();
my $old_diskspace = $$Event{DiskSpace}; my $old_diskspace = $$Event{DiskSpace};
if ( $old_diskspace != $Event->DiskSpace(undef) ) { my $new_diskspace = $Event->DiskSpace(undef);
if (
( (!defined $old_diskspace) and defined $new_diskspace)
or
( (defined $old_diskspace) and (defined $new_diskspace) and ( $old_diskspace != $Event->DiskSpace(undef) ) )
) {
$Event->save(); $Event->save();
} }
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
@ -393,25 +398,25 @@ sub generateVideo {
chomp( $output ); chomp( $output );
my $status = $? >> 8; my $status = $? >> 8;
if ( $status || logDebugging() ) { if ( $status || logDebugging() ) {
Debug( "Output: $output\n" ); Debug("Output: $output\n");
} }
if ( $status ) { if ( $status ) {
Error( "Video generation '$command' failed with status: $status\n" ); Error("Video generation '$command' failed with status: $status\n");
if ( wantarray() ) { if ( wantarray() ) {
return( undef, undef ); return( undef, undef );
} }
return( 0 ); return 0;
} else { } else {
my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?'; my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute( $event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal("Unable toexecute '$sql': ".$sth->errstr());
if ( wantarray() ) { if ( wantarray() ) {
return( $format, $output ); return( $format, $output );
} }
} }
return( 1 ); return 1;
} }
# Returns an image absolute path for given event and frame # Returns an image absolute path for given event and frame
@ -419,13 +424,14 @@ sub generateVideo {
# If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists # If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists
# An empty string is returned if no one from methods above works # An empty string is returned if no one from methods above works
sub generateImage { sub generateImage {
my $event = shift; my $Event = shift;
my $frame = shift; my $frame = shift;
my $analyse = do { @_ ? shift : 0 }; # don't return analyse image by default my $analyse = @_ ? shift : 0; # don't return analyse image by default
my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', getEventPath($event), $frame->{FrameId}); my $event_path = $Event->Path();
my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', getEventPath($event), $frame->{FrameId}) if $analyse; my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', $event_path, $frame->{FrameId});
my $video_path = sprintf('%s/%d-video.mp4', getEventPath($event), $event->{Id}); my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', $event_path, $frame->{FrameId}) if $analyse;
my $video_path = sprintf('%s/%d-video.mp4', $event_path, $Event->{Id});
my $image_path = ''; my $image_path = '';
# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video # check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video
@ -433,8 +439,18 @@ sub generateImage {
$image_path = $analyse_image_path; $image_path = $analyse_image_path;
} elsif ( -r $capture_image_path ) { } elsif ( -r $capture_image_path ) {
$image_path = $capture_image_path; $image_path = $capture_image_path;
} elsif ( -e $video_path ) { } elsif ( -r $video_path ) {
if ( !system('ffmpeg -y -v 0 -i '.$video_path." -vf 'select=gte(n\\,".$frame->{FrameId}."),setpts=PTS-STARTPTS' -vframes 1 -f image2 ".$capture_image_path) ) { my $command ="ffmpeg -ss $$frame{Delta} -i '$video_path' -frames:v 1 '$capture_image_path'";
#$command = "ffmpeg -y -v 0 -i $video_path -vf 'select=gte(n\\,$$frame{FrameId}),setpts=PTS-STARTPTS' -vframes 1 -f image2 $capture_image_path";
my $output = qx($command);
chomp( $output );
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug("Output: $output\n");
}
if ( $status ) {
Error("Failed $command status $status");
} else {
$image_path = $capture_image_path; $image_path = $capture_image_path;
} }
} }
@ -581,7 +597,7 @@ sub uploadArchFile {
sub substituteTags { sub substituteTags {
my $text = shift; my $text = shift;
my $filter = shift; my $filter = shift;
my $event = shift; my $Event = shift;
my $attachments_ref = shift; my $attachments_ref = shift;
# First we'd better check what we need to get # First we'd better check what we need to get
@ -589,35 +605,7 @@ sub substituteTags {
# monitor information? # monitor information?
my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
my $monitor = {}; my $Monitor = $Event->Monitor() if $need_monitor;
if ( $need_monitor ) {
my $db_now = strftime( '%Y-%m-%d %H:%M:%S', localtime() );
my $sql = "SELECT
M.Id,
count(E.Id) as EventCount,
count(if(E.Archived,1,NULL))
as ArchEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL))
as HourEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL))
as DayEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL))
as WeekEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL))
as MonthEventCount
FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id
WHERE MonitorId = ?
GROUP BY E.MonitorId
ORDER BY Id"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{MonitorId} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
$monitor = $sth->fetchrow_hashref();
$sth->finish();
return() if ( !$monitor );
}
# Do we need the image information too? # Do we need the image information too?
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/; my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/;
@ -631,8 +619,9 @@ sub substituteTags {
; ;
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute( $Event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
my $rows = 0;
while( my $frame = $sth->fetchrow_hashref() ) { while( my $frame = $sth->fetchrow_hashref() ) {
if ( !$first_alarm_frame ) { if ( !$first_alarm_frame ) {
$first_alarm_frame = $frame; $first_alarm_frame = $frame;
@ -641,62 +630,68 @@ sub substituteTags {
$max_alarm_frame = $frame; $max_alarm_frame = $frame;
$max_alarm_score = $frame->{Score}; $max_alarm_score = $frame->{Score};
} }
$rows ++;
} }
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alaarm_frame: $max_alarm_frame, score: $max_alarm_score");
$sth->finish(); $sth->finish();
} }
my $url = $Config{ZM_URL}; my $url = $Config{ZM_URL};
$text =~ s/%ZP%/$url/g; $text =~ s/%ZP%/$url/g;
$text =~ s/%MN%/$event->{MonitorName}/g; $text =~ s/%MN%/$Event->{MonitorName}/g;
$text =~ s/%MET%/$monitor->{EventCount}/g; $text =~ s/%MET%/$Monitor->{TotalEvents}/g;
$text =~ s/%MEH%/$monitor->{HourEventCount}/g; $text =~ s/%MEH%/$Monitor->{HourEvents}/g;
$text =~ s/%MED%/$monitor->{DayEventCount}/g; $text =~ s/%MED%/$Monitor->{DayEvents}/g;
$text =~ s/%MEW%/$monitor->{WeekEventCount}/g; $text =~ s/%MEW%/$Monitor->{WeekEvents}/g;
$text =~ s/%MEM%/$monitor->{MonthEventCount}/g; $text =~ s/%MEM%/$Monitor->{MonthEvents}/g;
$text =~ s/%MEA%/$monitor->{ArchEventCount}/g; $text =~ s/%MEA%/$Monitor->{ArchivedEvents}/g;
$text =~ s/%MP%/$url?view=watch&mid=$event->{MonitorId}/g; $text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
$text =~ s/%MPS%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=stream/g; $text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
$text =~ s/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/g; $text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
$text =~ s/%EP%/$url?view=event&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EP%/$url?view=event&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPS%/$url?view=event&mode=stream&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPS%/$url?view=event&mode=stream&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPI%/$url?view=event&mode=still&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EI%/$event->{Id}/g; $text =~ s/%EI%/$Event->{Id}/g;
$text =~ s/%EN%/$event->{Name}/g; $text =~ s/%EN%/$Event->{Name}/g;
$text =~ s/%EC%/$event->{Cause}/g; $text =~ s/%EC%/$Event->{Cause}/g;
$text =~ s/%ED%/$event->{Notes}/g; $text =~ s/%ED%/$Event->{Notes}/g;
$text =~ s/%ET%/$event->{StartTime}/g; $text =~ s/%ET%/$Event->{StartTime}/g;
$text =~ s/%EL%/$event->{Length}/g; $text =~ s/%EL%/$Event->{Length}/g;
$text =~ s/%EF%/$event->{Frames}/g; $text =~ s/%EF%/$Event->{Frames}/g;
$text =~ s/%EFA%/$event->{AlarmFrames}/g; $text =~ s/%EFA%/$Event->{AlarmFrames}/g;
$text =~ s/%EST%/$event->{TotScore}/g; $text =~ s/%EST%/$Event->{TotScore}/g;
$text =~ s/%ESA%/$event->{AvgScore}/g; $text =~ s/%ESA%/$Event->{AvgScore}/g;
$text =~ s/%ESM%/$event->{MaxScore}/g; $text =~ s/%ESM%/$Event->{MaxScore}/g;
if ( $first_alarm_frame ) { if ( $first_alarm_frame ) {
$text =~ s/%EPI1%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$first_alarm_frame->{FrameId}/g; $text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
$text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; $text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
if ( $attachments_ref && $text =~ s/%EI1%//g ) { if ( $attachments_ref && $text =~ s/%EI1%//g ) {
my $path = generateImage( $event, $first_alarm_frame ); my $path = generateImage($Event, $first_alarm_frame);
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} }
} }
if ( $attachments_ref && $text =~ s/%EIM%//g ) { if ( $attachments_ref && ( $text =~ s/%EIM%//g ) ) {
# Don't attach the same image twice # Don't attach the same image twice
if ( !@$attachments_ref if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) || ( $first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
) { ) {
my $path = generateImage( $event, $max_alarm_frame ); my $path = generateImage($Event, $max_alarm_frame);
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIM");
} }
} }
} }
if ( $attachments_ref && $text =~ s/%EI1A%//g ) { if ( $attachments_ref && $text =~ s/%EI1A%//g ) {
my $path = generateImage( $event, $first_alarm_frame, 'analyse' ); my $path = generateImage($Event, $first_alarm_frame, 'analyse');
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EI1A");
} }
} }
if ( $attachments_ref && $text =~ s/%EIMA%//g ) { if ( $attachments_ref && $text =~ s/%EIMA%//g ) {
@ -704,9 +699,11 @@ sub substituteTags {
if ( !@$attachments_ref if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
) { ) {
my $path = generateImage( $event, $max_alarm_frame, 'analyse'); my $path = generateImage($Event, $max_alarm_frame, 'analyse');
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIMA");
} }
} }
} }
@ -714,49 +711,49 @@ sub substituteTags {
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) { if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) {
if ( $text =~ s/%EV%//g ) { if ( $text =~ s/%EV%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event ); my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) { if ( !$format ) {
return( undef ); return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push( @$attachments_ref, { type=>"video/$format", path=>$path } );
} }
if ( $text =~ s/%EVM%//g ) { if ( $text =~ s/%EVM%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event, 1 ); my ( $format, $path ) = generateVideo($filter, $Event, 1);
if ( !$format ) { if ( !$format ) {
return( undef ); return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push @$attachments_ref, { type=>"video/$format", path=>$path };
} }
} }
$text =~ s/%FN%/$filter->{Name}/g; $text =~ s/%FN%/$filter->{Name}/g;
( my $filter_name = $filter->{Name} ) =~ s/ /+/g; ( my $filter_name = $filter->{Name} ) =~ s/ /+/g;
$text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; $text =~ s/%FP%/$url?view=filter&mid=$Event->{MonitorId}&filter_name=$filter_name/g;
return( $text ); return $text;
} # end subsitituteTags } # end subsitituteTags
sub sendEmail { sub sendEmail {
my $filter = shift; my $filter = shift;
my $event = shift; my $Event = shift;
if ( ! $Config{ZM_FROM_EMAIL} ) { if ( ! $Config{ZM_FROM_EMAIL} ) {
Error( "No 'from' email address defined, not sending email" ); Error("No 'from' email address defined, not sending email");
return( 0 ); return 0;
} }
if ( ! $Config{ZM_EMAIL_ADDRESS} ) { if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
Error( 'No email address defined, not sending email' ); Error('No email address defined, not sending email');
return( 0 ); return 0;
} }
Info( "Creating notification email\n" ); Info("Creating notification email\n");
my $subject = substituteTags( $Config{ZM_EMAIL_SUBJECT}, $filter, $event ); my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
return( 0 ) if ( !$subject ); return 0 if !$subject;
my @attachments; my @attachments;
my $body = substituteTags( $Config{ZM_EMAIL_BODY}, $filter, $event, \@attachments ); my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
return( 0 ) if ( !$body ); return 0 if !$body;
Info( "Sending notification email '$subject'\n" ); Info("Sending notification email '$subject'\n");
eval { eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) { if ( $Config{ZM_NEW_MAIL_MODULES} ) {
@ -829,9 +826,9 @@ sub sendEmail {
Info( "Notification email sent\n" ); Info( "Notification email sent\n" );
} }
my $sql = 'update Events set Emailed = 1 where Id = ?'; my $sql = 'update Events set Emailed = 1 where Id = ?';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached($sql)
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
return( 1 ); return( 1 );

View File

@ -211,7 +211,7 @@ if ( $command =~ /^(?:start|restart)$/ ) {
my $res = $sth->execute( @values ) my $res = $sth->execute( @values )
or Fatal( "Can't execute: ".$sth->errstr() ); or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) { while( my $monitor = $sth->fetchrow_hashref() ) {
if ( $monitor->{Function} ne 'None' ) { if ( $monitor->{Function} ne 'None' && $monitor->{Type} ne 'WebSite' ) {
if ( $monitor->{Type} eq 'Local' ) { if ( $monitor->{Type} eq 'Local' ) {
runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); runCommand( "zmdc.pl start zmc -d $monitor->{Device}" );
} else { } else {

View File

@ -84,6 +84,7 @@ while( 1 ) {
while( my $monitor = $sth->fetchrow_hashref() ) { while( my $monitor = $sth->fetchrow_hashref() ) {
my $now = time(); my $now = time();
next if $monitor->{Function} eq 'None'; next if $monitor->{Function} eq 'None';
next if $monitor->{Type} eq 'WebSite';
my $restart = 0; my $restart = 0;
if ( zmMemVerify( $monitor ) ) { if ( zmMemVerify( $monitor ) ) {
# Check we have got an image recently # Check we have got an image recently
@ -147,11 +148,11 @@ while( 1 ) {
if ( !defined($image_time) ) { if ( !defined($image_time) ) {
# Can't read from shared data # Can't read from shared data
$restart = 1; $restart = 1;
Error( "Error reading shared data for $$monitor{Id} $$monitor{Name}\n"); Error("Error reading shared data for $$monitor{Id} $$monitor{Name}\n");
} elsif ( !$image_time ) { } elsif ( !$image_time ) {
# We can't get the last capture time so can't be sure it's died. # We can't get the last capture time so can't be sure it's died.
$restart = 1; $restart = 1;
Error( "Error getting last analyse time for $$monitor{Id} $$monitor{Name}\n"); Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.\n");
} else { } else {
my $max_image_delay = ( $monitor->{MaxFPS} my $max_image_delay = ( $monitor->{MaxFPS}

View File

@ -53,6 +53,8 @@ protected:
int contrast; int contrast;
bool capture; bool capture;
bool record_audio; bool record_audio;
unsigned int bytes;
int mVideoStreamId; int mVideoStreamId;
int mAudioStreamId; int mAudioStreamId;
@ -93,6 +95,7 @@ public:
unsigned int SubpixelOrder() const { return( subpixelorder ); } unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); } unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); } unsigned int ImageSize() const { return( imagesize ); }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
@ -113,6 +116,7 @@ public:
virtual AVCodecContext *get_AudioCodecContext() { return NULL; }; virtual AVCodecContext *get_AudioCodecContext() { return NULL; };
int get_VideoStreamId() { return mVideoStreamId; }; int get_VideoStreamId() { return mVideoStreamId; };
int get_AudioStreamId() { return mAudioStreamId; }; int get_AudioStreamId() { return mAudioStreamId; };
virtual int Close()=0;
}; };
#endif // ZM_CAMERA_H #endif // ZM_CAMERA_H

View File

@ -73,6 +73,7 @@ public:
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int Close() { return 0; };
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();

View File

@ -23,7 +23,6 @@
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
// From what I read, we need one of these per thread
MYSQL dbconn; MYSQL dbconn;
Mutex db_mutex; Mutex db_mutex;
@ -31,8 +30,11 @@ bool zmDbConnected = false;
bool zmDbConnect() { bool zmDbConnect() {
// For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu // For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu
//if ( zmDbConnected ) // But they really need to be here in order to prevent a double open of mysql
//return; if ( zmDbConnected ) {
Warning("Calling zmDbConnect when already connected");
return true;
}
if ( !mysql_init(&dbconn) ) { if ( !mysql_init(&dbconn) ) {
Error("Can't initialise database connection: %s", mysql_error(&dbconn)); Error("Can't initialise database connection: %s", mysql_error(&dbconn));

View File

@ -27,6 +27,7 @@
#include <getopt.h> #include <getopt.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -149,7 +150,7 @@ Event::Event(
time_path_ptr += snprintf(time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i]); time_path_ptr += snprintf(time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i]);
} }
// Create event id symlink // Create event id symlink
std::string id_file = stringtf("%s/.%llu", date_path, id); std::string id_file = stringtf("%s/.%" PRIu64, date_path, id);
if ( symlink(time_path, id_file.c_str()) < 0 ) if ( symlink(time_path, id_file.c_str()) < 0 )
Error("Can't symlink %s -> %s: %s", id_file.c_str(), path.c_str(), strerror(errno)); Error("Can't symlink %s -> %s: %s", id_file.c_str(), path.c_str(), strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) { } else if ( storage->Scheme() == Storage::MEDIUM ) {
@ -161,7 +162,7 @@ Event::Event(
if ( errno != EEXIST ) if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
} }
path += stringtf("/%llu", id); path += stringtf("/%" PRIu64, id);
if ( mkdir(path.c_str(), 0755) ) { if ( mkdir(path.c_str(), 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area. // FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) if ( errno != EEXIST )
@ -169,7 +170,7 @@ Event::Event(
} }
} else { } else {
// Shallow Storage // Shallow Storage
path = stringtf("%s/%d/%llu", storage->Path(), monitor->Id(), id); path = stringtf("%s/%d/%" PRIu64, storage->Path(), monitor->Id(), id);
if ( mkdir(path.c_str(), 0755) ) { if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST ) { if ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
@ -177,7 +178,7 @@ Event::Event(
} }
// Create empty id tag file // Create empty id tag file
std::string id_file = stringtf("%s/.%llu", path, id); std::string id_file = stringtf("%s/.%" PRIu64, path, id);
if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) if ( FILE *id_fp = fopen(id_file.c_str(), "w") )
fclose(id_fp); fclose(id_fp);
else else
@ -238,7 +239,7 @@ Event::~Event() {
if ( frames > last_db_frame ) { if ( frames > last_db_frame ) {
Debug(1, "Adding closing frame %d to DB", frames); Debug(1, "Adding closing frame %d to DB", frames);
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %llu, %d, from_unixtime( %ld ), %s%ld.%02ld )", "INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %" PRIu64 ", %d, from_unixtime( %ld ), %s%ld.%02ld )",
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec); id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec);
db_mutex.lock(); db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
@ -250,7 +251,7 @@ Event::~Event() {
} }
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"UPDATE Events SET Name='%s %llu', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %llu", "UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
db_mutex.lock(); db_mutex.lock();
while ( mysql_query(&dbconn, sql) ) { while ( mysql_query(&dbconn, sql) ) {
@ -397,7 +398,7 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length()); mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
snprintf(sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %llu", escapedNotes, id); snprintf(sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id);
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s", mysql_error( &dbconn ) ); Error( "Can't insert event: %s", mysql_error( &dbconn ) );
@ -456,7 +457,7 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
} }
int sql_len = strlen(sql); int sql_len = strlen(sql);
snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %llu, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %" PRIu64 ", %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
frameCount++; frameCount++;
} }
@ -531,7 +532,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
Debug(1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type]); Debug(1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type]);
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf(sql, sizeof(sql), "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %llu, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score); snprintf(sql, sizeof(sql), "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score);
db_mutex.lock(); db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frame: %s", mysql_error(&dbconn)); Error("Can't insert frame: %s", mysql_error(&dbconn));
@ -545,7 +546,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
// We are writing a Bulk frame // We are writing a Bulk frame
if ( frame_type == BULK ) { if ( frame_type == BULK ) {
snprintf( sql, sizeof(sql), snprintf( sql, sizeof(sql),
"UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %llu", "UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %" PRIu64,
( delta_time.positive?"":"-" ), ( delta_time.positive?"":"-" ),
delta_time.sec, delta_time.fsec, delta_time.sec, delta_time.fsec,
frames, frames,

View File

@ -75,7 +75,7 @@ class Event {
static int pre_alarm_count; static int pre_alarm_count;
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
unsigned long long int id; uint64_t id;
Monitor *monitor; Monitor *monitor;
struct timeval start_time; struct timeval start_time;
struct timeval end_time; struct timeval end_time;
@ -102,7 +102,7 @@ class Event {
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
~Event(); ~Event();
unsigned long long int Id() const { return id; } uint64_t Id() const { return id; }
const std::string &Cause() { return cause; } const std::string &Cause() { return cause; }
int Frames() const { return frames; } int Frames() const { return frames; }
int AlarmFrames() const { return alarm_frames; } int AlarmFrames() const { return alarm_frames; }

View File

@ -27,6 +27,7 @@
#include <getopt.h> #include <getopt.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -62,7 +63,7 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
exit( mysql_errno(&dbconn)); exit( mysql_errno(&dbconn));
} }
unsigned long long init_event_id = atoll(dbrow[0]); uint64_t init_event_id = atoll(dbrow[0]);
mysql_free_result(result); mysql_free_result(result);
@ -87,7 +88,7 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
return true; return true;
} }
bool EventStream::loadInitialEventData( unsigned long long init_event_id, unsigned int init_frame_id ) { bool EventStream::loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id ) {
loadEventData(init_event_id); loadEventData(init_event_id);
if ( init_frame_id ) { if ( init_frame_id ) {
@ -105,10 +106,10 @@ bool EventStream::loadInitialEventData( unsigned long long init_event_id, unsign
return true; return true;
} }
bool EventStream::loadEventData(unsigned long long event_id) { bool EventStream::loadEventData(uint64_t event_id) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %llu", event_id); snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %" PRIu64, event_id);
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
@ -167,26 +168,26 @@ bool EventStream::loadEventData(unsigned long long event_id) {
} else if ( event_data->scheme == Storage::MEDIUM ) { } else if ( event_data->scheme == Storage::MEDIUM ) {
struct tm *event_time = localtime( &event_data->start_time ); struct tm *event_time = localtime( &event_data->start_time );
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%04d-%02d-%02d/%llu", snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%04d-%02d-%02d/%" PRIu64,
storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id ); storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id );
else else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%04d-%02d-%02d/%llu", snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%04d-%02d-%02d/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
event_data->event_id ); event_data->event_id );
} else { } else {
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%llu", snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64,
storage_path, event_data->monitor_id, event_data->event_id ); storage_path, event_data->monitor_id, event_data->event_id );
else else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%llu", snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id ); staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id );
} }
delete storage; storage = NULL; delete storage; storage = NULL;
updateFrameRate( (double)event_data->frame_count/event_data->duration ); updateFrameRate( (double)event_data->frame_count/event_data->duration );
snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames where EventId = %llu ORDER BY FrameId ASC", event_id); snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames where EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id);
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn)); exit(mysql_errno(&dbconn));
@ -254,7 +255,7 @@ bool EventStream::loadEventData(unsigned long long event_id) {
else else
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
} }
Debug(2, "Event:%llu, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration); Debug(2, "Event:%" PRIu64 ", Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration);
return true; return true;
} // bool EventStream::loadEventData( int event_id ) } // bool EventStream::loadEventData( int event_id )
@ -450,7 +451,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
break; break;
} }
struct { struct {
unsigned long long event_id; uint64_t event_id;
int progress; int progress;
int rate; int rate;
int zoom; int zoom;
@ -462,7 +463,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
status_data.rate = replay_rate; status_data.rate = replay_rate;
status_data.zoom = zoom; status_data.zoom = zoom;
status_data.paused = paused; status_data.paused = paused;
Debug( 2, "Event:%llu, Paused:%d, progress:%d Rate:%d, Zoom:%d", Debug( 2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d",
status_data.event_id, status_data.event_id,
status_data.paused, status_data.paused,
status_data.progress, status_data.progress,
@ -492,10 +493,10 @@ void EventStream::checkEventLoaded() {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
if ( curr_frame_id <= 0 ) { if ( curr_frame_id <= 0 ) {
snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id < %llu ORDER BY Id DESC LIMIT 1", event_data->monitor_id, event_data->event_id ); snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id < %" PRIu64 " ORDER BY Id DESC LIMIT 1", event_data->monitor_id, event_data->event_id );
reload_event = true; reload_event = true;
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) { } else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %llu ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id ); snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id );
reload_event = true; reload_event = true;
} }
@ -520,8 +521,8 @@ void EventStream::checkEventLoaded() {
} }
if ( dbrow ) { if ( dbrow ) {
unsigned long long event_id = atoll(dbrow[0]); uint64_t event_id = atoll(dbrow[0]);
Debug( 1, "Loading new event %llu", event_id ); Debug( 1, "Loading new event %" PRIu64, event_id );
loadEventData(event_id); loadEventData(event_id);
@ -846,7 +847,7 @@ void EventStream::runStream() {
closeComms(); closeComms();
} }
void EventStream::setStreamStart( unsigned long long init_event_id, unsigned int init_frame_id=0 ) { void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) {
loadInitialEventData( init_event_id, init_frame_id ); loadInitialEventData( init_event_id, init_frame_id );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id ); Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );

View File

@ -54,7 +54,7 @@ class EventStream : public StreamBase {
}; };
struct EventData { struct EventData {
unsigned long long event_id; uint64_t event_id;
unsigned long monitor_id; unsigned long monitor_id;
unsigned long storage_id; unsigned long storage_id;
unsigned long frame_count; unsigned long frame_count;
@ -83,8 +83,8 @@ class EventStream : public StreamBase {
FFmpeg_Input *ffmpeg_input; FFmpeg_Input *ffmpeg_input;
protected: protected:
bool loadEventData( unsigned long long event_id ); bool loadEventData( uint64_t event_id );
bool loadInitialEventData( unsigned long long init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id );
bool loadInitialEventData( int monitor_id, time_t event_time ); bool loadInitialEventData( int monitor_id, time_t event_time );
void checkEventLoaded(); void checkEventLoaded();
@ -110,7 +110,7 @@ class EventStream : public StreamBase {
ffmpeg_input = NULL; ffmpeg_input = NULL;
} }
void setStreamStart( unsigned long long init_event_id, unsigned int init_frame_id ); void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
void setStreamStart( int monitor_id, time_t event_time ); void setStreamStart( int monitor_id, time_t event_time );
void setStreamMode( StreamMode p_mode ) { void setStreamMode( StreamMode p_mode ) {
mode = p_mode; mode = p_mode;

View File

@ -16,6 +16,7 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <cinttypes>
#include "zm_ffmpeg.h" #include "zm_ffmpeg.h"
#include "zm_image.h" #include "zm_image.h"
@ -27,9 +28,9 @@ void FFMPEGInit() {
static bool bInit = false; static bool bInit = false;
if ( !bInit ) { if ( !bInit ) {
if ( logDebugging() ) //if ( logDebugging() )
av_log_set_level( AV_LOG_DEBUG ); //av_log_set_level( AV_LOG_DEBUG );
else //else
av_log_set_level( AV_LOG_QUIET ); av_log_set_level( AV_LOG_QUIET );
av_register_all(); av_register_all();
avformat_network_init(); avformat_network_init();

View File

@ -113,7 +113,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
FfmpegCamera::~FfmpegCamera() { FfmpegCamera::~FfmpegCamera() {
CloseFfmpeg(); Close();
avformat_network_deinit(); avformat_network_deinit();
} }
@ -121,7 +121,7 @@ FfmpegCamera::~FfmpegCamera() {
int FfmpegCamera::PrimeCapture() { int FfmpegCamera::PrimeCapture() {
if ( mCanCapture ) { if ( mCanCapture ) {
Info("Priming capture from %s, Closing", mPath.c_str()); Info("Priming capture from %s, Closing", mPath.c_str());
CloseFfmpeg(); Close();
} }
mVideoStreamId = -1; mVideoStreamId = -1;
mAudioStreamId = -1; mAudioStreamId = -1;
@ -166,6 +166,7 @@ int FfmpegCamera::Capture(ZMPacket &zm_packet) {
return 0; return 0;
} }
bytes += packet.size;
zm_packet.set_packet(&packet); zm_packet.set_packet(&packet);
zm_av_packet_unref(&packet); zm_av_packet_unref(&packet);
return 1; return 1;
@ -435,7 +436,7 @@ int FfmpegCamera::OpenFfmpeg() {
return 1; return 1;
} // int FfmpegCamera::OpenFfmpeg() } // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::CloseFfmpeg() { int FfmpegCamera::Close() {
Debug(2, "CloseFfmpeg called."); Debug(2, "CloseFfmpeg called.");
@ -476,6 +477,6 @@ int FfmpegCamera::CloseFfmpeg() {
} }
return 0; return 0;
} // end int FfmpegCamera::CloseFfmpeg() } // end FfmpegCamera::Close
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -69,7 +69,7 @@ class FfmpegCamera : public Camera {
AVPacket packet; AVPacket packet;
int OpenFfmpeg(); int OpenFfmpeg();
int CloseFfmpeg(); int Close();
bool mCanCapture; bool mCanCapture;
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -58,6 +58,7 @@ public:
int PreCapture(); int PreCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
int Close() { return 0; };
}; };
#endif // ZM_FILE_CAMERA_H #endif // ZM_FILE_CAMERA_H

View File

@ -69,6 +69,7 @@ public:
int PreCapture(); int PreCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
int Close() { return 0; };
}; };
#endif // HAVE_LIBVLC #endif // HAVE_LIBVLC

View File

@ -158,6 +158,7 @@ public:
int PreCapture(); int PreCapture();
int Capture(ZMPacket &p); int Capture(ZMPacket &p);
int PostCapture(); int PostCapture();
int Close() { return 0; };
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
AVStream* get_VideoStream(); AVStream* get_VideoStream();
}; };

View File

@ -21,6 +21,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -77,7 +78,7 @@ std::string load_monitor_sql =
"EventPrefix, LabelFormat, LabelX, LabelY, LabelSize," "EventPrefix, LabelFormat, LabelX, LabelY, LabelSize,"
"ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, " "ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, "
"SectionLength, FrameSkip, MotionFrameSkip, " "SectionLength, FrameSkip, MotionFrameSkip, "
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, SignalCheckColour FROM Monitors"; "FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, SignalCheckPoints, SignalCheckColour FROM Monitors";
std::string CameraType_Strings[] = { std::string CameraType_Strings[] = {
"Local", "Local",
@ -316,6 +317,7 @@ Monitor::Monitor()
ref_blend_perc(0), ref_blend_perc(0),
alarm_ref_blend_perc(0), alarm_ref_blend_perc(0),
track_motion(0), track_motion(0),
signal_check_points(0),
signal_check_colour(0), signal_check_colour(0),
embed_exif(0), embed_exif(0),
purpose(QUERY), purpose(QUERY),
@ -543,7 +545,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
+ (image_buffer_count * width * height * colours) + (image_buffer_count * width * height * colours)
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */ + 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
Debug(1, "mem.size=%d", mem_size); Debug(1, "mem.size SharedData=%d TriggerData=%d VideoStoreData=%d total=%d",
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData), mem_size);
mem_ptr = NULL; mem_ptr = NULL;
Zone **zones = 0; Zone **zones = 0;
@ -551,6 +554,13 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
this->AddZones(n_zones, zones); this->AddZones(n_zones, zones);
this->AddPrivacyBitmask(zones); this->AddPrivacyBitmask(zones);
// maybe unneeded
storage = new Storage(storage_id);
Debug(1, "Storage path: %s", storage->Path());
// Should maybe store this for later use
char monitor_dir[PATH_MAX] = "";
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
//this0>delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), //this0>delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), //ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
Debug(1, "Loaded monitor %d(%s), %d zones", id, name, n_zones); Debug(1, "Loaded monitor %d(%s), %d zones", id, name, n_zones);
@ -824,7 +834,7 @@ bool Monitor::connect() {
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64))); shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
} }
Debug(3, "Allocating %d image buffers", image_buffer_count ); Debug(3, "Allocating %d image buffers", image_buffer_count);
image_buffer = new ZMPacket[image_buffer_count]; image_buffer = new ZMPacket[image_buffer_count];
for ( int i = 0; i < image_buffer_count; i++ ) { for ( int i = 0; i < image_buffer_count; i++ ) {
image_buffer[i].image_index = i; image_buffer[i].image_index = i;
@ -887,7 +897,7 @@ Monitor::~Monitor() {
if ( mem_ptr ) { if ( mem_ptr ) {
if ( event ) { if ( event ) {
Info( "%s: image_count:%d - Closing event %llu, shutting down", name, image_count, event->Id() ); Info( "%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id() );
closeEvent(); closeEvent();
// closeEvent may start another thread to close the event, so wait for it to finish // closeEvent may start another thread to close the event, so wait for it to finish
@ -1082,7 +1092,7 @@ unsigned int Monitor::GetLastWriteIndex() const {
} }
uint64_t Monitor::GetLastEventId() const { uint64_t Monitor::GetLastEventId() const {
Debug(2, "mem_ptr(%x), size(%d) State(%d) last_read_index(%d) last_read_time(%d) last_event(%llu)", Debug(2, "mem_ptr(%x), size(%d) State(%d) last_read_index(%d) last_read_time(%d) last_event(%" PRIu64 ")",
mem_ptr, mem_ptr,
shared_data->size, shared_data->size,
shared_data->state, shared_data->state,
@ -1105,7 +1115,7 @@ double Monitor::GetFPS() const {
ZMPacket *snap1 = &image_buffer[index1]; ZMPacket *snap1 = &image_buffer[index1];
if ( !snap1->timestamp->tv_sec ) { if ( !snap1->timestamp->tv_sec ) {
// This should be impossible // This should be impossible
Warning("Impossible situation. No timestamp on captured image"); Warning("Impossible situation. No timestamp on captured image index was %d, image-buffer_count was (%d)", index1, image_buffer_count);
return 0.0; return 0.0;
} }
struct timeval time1 = *snap1->timestamp; struct timeval time1 = *snap1->timestamp;
@ -1196,9 +1206,9 @@ void Monitor::actionReload() {
void Monitor::actionEnable() { void Monitor::actionEnable() {
shared_data->action |= RELOAD; shared_data->action |= RELOAD;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE Monitors SET Enabled = 1 WHERE Id = %d", id); snprintf(sql, sizeof(sql), "UPDATE Monitors SET Enabled = 1 WHERE Id = %d", id);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
} }
@ -1374,7 +1384,7 @@ void Monitor::DumpZoneImage( const char *zone_string ) {
if ( eventid_row.fetch(sql.c_str()) ) { if ( eventid_row.fetch(sql.c_str()) ) {
uint64_t event_id = atoll(eventid_row[0]); uint64_t event_id = atoll(eventid_row[0]);
Debug(3, "Got event %llu", event_id); Debug(3, "Got event %" PRIu64, event_id);
EventStream *stream = new EventStream(); EventStream *stream = new EventStream();
stream->setStreamStart(event_id, (unsigned int)1); stream->setStreamStart(event_id, (unsigned int)1);
zone_image = stream->getImage(); zone_image = stream->getImage();
@ -1446,7 +1456,7 @@ bool Monitor::CheckSignal( const Image *image ) {
static Rgb colour_val; /* RGB32 color */ static Rgb colour_val; /* RGB32 color */
static int usedsubpixorder; static int usedsubpixorder;
if ( config.signal_check_points > 0 ) { if ( signal_check_points > 0 ) {
if ( static_undef ) { if ( static_undef ) {
static_undef = false; static_undef = false;
usedsubpixorder = camera->SubpixelOrder(); usedsubpixorder = camera->SubpixelOrder();
@ -1464,8 +1474,9 @@ bool Monitor::CheckSignal( const Image *image ) {
int colours = image->Colours(); int colours = image->Colours();
int index = 0; int index = 0;
for ( int i = 0; i < config.signal_check_points; i++ ) { for ( int i = 0; i < signal_check_points; i++ ) {
while( true ) { while( true ) {
// Why the casting to long long? also note that on a 64bit cpu, long long is 128bits
index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX);
if ( !config.timestamp_on_capture || !label_format[0] ) if ( !config.timestamp_on_capture || !label_format[0] )
break; break;
@ -1501,6 +1512,7 @@ bool Monitor::CheckSignal( const Image *image ) {
} }
} }
} // end for < signal_check_points } // end for < signal_check_points
Debug(1,"SignalCheck: %d points, colour_val(%d)", signal_check_points, colour_val);
return false; return false;
} // end if signal_check_points } // end if signal_check_points
return true; return true;
@ -1650,7 +1662,7 @@ bool Monitor::Analyse() {
if ( !signal ) { if ( !signal ) {
signalText = "Lost"; signalText = "Lost";
if ( event ) { if ( event ) {
Info( "%s: %03d - Closing event %d, signal loss", name, analysis_image_count, event->Id() ); Info( "%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id() );
closeEvent(); closeEvent();
last_section_mod = 0; last_section_mod = 0;
} }
@ -1757,7 +1769,7 @@ bool Monitor::Analyse() {
alarm_cause = cause+" "+alarm_cause; alarm_cause = cause+" "+alarm_cause;
strncpy( shared_data->alarm_cause,alarm_cause.c_str() , sizeof(shared_data->alarm_cause) ); strncpy( shared_data->alarm_cause,alarm_cause.c_str() , sizeof(shared_data->alarm_cause) );
video_store_data->recording = event->StartTime(); video_store_data->recording = event->StartTime();
Info( "%s: %03d - Opening new event %llu, section start", name, analysis_image_count, event->Id() ); Info( "%s: %03d - Opening new event %" PRIu64 ", section start", name, analysis_image_count, event->Id() );
/* To prevent cancelling out an existing alert\prealarm\alarm state */ /* To prevent cancelling out an existing alert\prealarm\alarm state */
if ( state == IDLE ) { if ( state == IDLE ) {
shared_data->state = state = TAPE; shared_data->state = state = TAPE;
@ -1798,15 +1810,15 @@ bool Monitor::Analyse() {
last_alarm_count = analysis_image_count; last_alarm_count = analysis_image_count;
} else { // no score? } else { // no score?
if ( state == ALARM ) { if ( state == ALARM ) {
Info( "%s: %03d - Gone into alert state", name, analysis_image_count ); Info("%s: %03d - Gone into alert state", name, analysis_image_count );
shared_data->state = state = ALERT; shared_data->state = state = ALERT;
} else if ( state == ALERT ) { } else if ( state == ALERT ) {
if ( analysis_image_count-last_alarm_count > post_event_count ) { if ( analysis_image_count-last_alarm_count > post_event_count ) {
Info( "%s: %03d - Left alarm state (%llu) - %d(%d) images", name, analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames() ); Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images", name, analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames() );
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
if ( (function != RECORD && function != MOCORD ) || event_close_mode == CLOSE_ALARM ) { if ( (function != RECORD && function != MOCORD ) || event_close_mode == CLOSE_ALARM ) {
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
Info( "%s: %03d - Closing event %llu, alarm end%s", name, analysis_image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); Info("%s: %03d - Closing event %" PRIu64 ", alarm end%s", name, analysis_image_count, event->Id(), (function==MOCORD)?", section truncated":"" );
closeEvent(); closeEvent();
} else { } else {
shared_data->state = state = TAPE; shared_data->state = state = TAPE;
@ -1881,7 +1893,7 @@ bool Monitor::Analyse() {
} else { } else {
Debug(3, "trigger == off"); Debug(3, "trigger == off");
if ( event ) { if ( event ) {
Info( "%s: %03d - Closing event %llu, trigger off", name, analysis_image_count, event->Id() ); Info("%s: %03d - Closing event %" PRIu64 ", trigger off", name, analysis_image_count, event->Id());
closeEvent(); closeEvent();
} }
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
@ -1935,7 +1947,7 @@ void Monitor::Reload() {
Debug(1, "Reloading monitor %s", name); Debug(1, "Reloading monitor %s", name);
if ( event ) { if ( event ) {
Info("%s: %03d - Closing event %llu, reloading", name, image_count, event->Id()); Info( "%s: %03d - Closing event %" PRIu64 ", reloading", name, image_count, event->Id() );
closeEvent(); closeEvent();
} }
@ -2001,8 +2013,8 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
if ( dest_ptr != link_id_str ) { if ( dest_ptr != link_id_str ) {
*dest_ptr = '\0'; *dest_ptr = '\0';
unsigned int link_id = atoi(link_id_str); unsigned int link_id = atoi(link_id_str);
if ( link_id > 0 && link_id != id) { if ( link_id > 0 && link_id != id ) {
Debug( 3, "Found linked monitor id %d", link_id ); Debug(3, "Found linked monitor id %d", link_id);
int j; int j;
for ( j = 0; j < n_link_ids; j++ ) { for ( j = 0; j < n_link_ids; j++ ) {
if ( link_ids[j] == link_id ) if ( link_ids[j] == link_id )
@ -2022,15 +2034,16 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
break; break;
} }
if ( n_link_ids > 0 ) { if ( n_link_ids > 0 ) {
Debug( 1, "Linking to %d monitors", n_link_ids ); Debug(1, "Linking to %d monitors", n_link_ids);
linked_monitors = new MonitorLink *[n_link_ids]; linked_monitors = new MonitorLink *[n_link_ids];
int count = 0; int count = 0;
for ( int i = 0; i < n_link_ids; i++ ) { for ( int i = 0; i < n_link_ids; i++ ) {
Debug(1, "Checking linked monitor %d", link_ids[i]); Debug(1, "Checking linked monitor %d", link_ids[i]);
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "SELECT Id, Name FROM Monitors WHERE Id = %d AND Function != 'None' AND Function != 'Monitor' AND Enabled = 1", link_ids[i] );
db_mutex.lock(); db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
db_mutex.lock();
snprintf(sql, sizeof(sql), "SELECT Id, Name FROM Monitors WHERE Id = %d AND Function != 'None' AND Function != 'Monitor' AND Enabled = 1", link_ids[i] );
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock(); db_mutex.unlock();
@ -2052,11 +2065,11 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]); Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]);
} }
mysql_free_result(result); mysql_free_result(result);
} // end foreach n_link_id } // end foreach link_id
n_linked_monitors = count; n_linked_monitors = count;
} // end if n_link_ids > 0 } // end if has link_ids
} } // end if p_linked_monitors
} } // end void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors)
int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose) { int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose) {
@ -2131,7 +2144,6 @@ int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose p
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
/* Returns 0 on success, even if no new images are available (transient error) /* Returns 0 on success, even if no new images are available (transient error)
* Returns -1 on failure. * Returns -1 on failure.
*/ */
@ -2285,7 +2297,7 @@ int Monitor::Capture() {
capture_image->Flip( orientation==FLIP_HORI ); capture_image->Flip( orientation==FLIP_HORI );
break; break;
} }
} } // end if have rotation
if ( privacy_bitmask ) if ( privacy_bitmask )
capture_image->MaskPrivacy( privacy_bitmask ); capture_image->MaskPrivacy( privacy_bitmask );
@ -2294,27 +2306,32 @@ int Monitor::Capture() {
TimestampImage( capture_image, packet->timestamp ); TimestampImage( capture_image, packet->timestamp );
} }
shared_data->signal = CheckSignal(capture_image); shared_data->signal = signal_check_points ? CheckSignal(capture_image) : true;
shared_data->last_write_index = index; shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
image_count++; image_count++;
packet->unlock(); packet->unlock();
if ( fps_report_interval && !(image_count%fps_report_interval) ) { if ( fps_report_interval && ( !(image_count%fps_report_interval) || image_count == 5 ) ) {
time_t now = image_buffer[index].timestamp->tv_sec; time_t now = image_buffer[index].timestamp->tv_sec;
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets. // If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
if ( now != last_fps_time ) { if ( now != last_fps_time ) {
// # of images per interval / the amount of time it took // # of images per interval / the amount of time it took
double new_capture_fps = double(fps_report_interval)/(now-last_fps_time); double new_capture_fps = double((image_count < fps_report_interval ? image_count : fps_report_interval))/(now-last_fps_time);
unsigned int new_camera_bytes = camera->Bytes();
unsigned int new_capture_bandwidth = (new_camera_bytes - last_camera_bytes)/(now-last_fps_time);
last_camera_bytes = new_camera_bytes;
//Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time );
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
Info("%s: %d - Capturing at %.2lf fps", name, image_count, new_capture_fps); Info("%s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", name, image_count, new_capture_fps, new_capture_bandwidth);
if ( new_capture_fps != capture_fps ) { if ( new_capture_fps != capture_fps ) {
capture_fps = new_capture_fps; capture_fps = new_capture_fps;
last_fps_time = now; last_fps_time = now;
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
db_mutex.lock(); db_mutex.lock();
snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,CaptureFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf", id, capture_fps, capture_fps); snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u",
id, capture_fps, new_capture_bandwidth, capture_fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
} }
@ -2330,6 +2347,7 @@ int Monitor::Capture() {
} // end if result } // end if result
} // end if deinterlacing } // end if deinterlacing
// Icon: I'm not sure these should be here. They have nothing to do with capturing // Icon: I'm not sure these should be here. They have nothing to do with capturing
if ( shared_data->action & GET_SETTINGS ) { if ( shared_data->action & GET_SETTINGS ) {
shared_data->brightness = camera->Brightness(); shared_data->brightness = camera->Brightness();
@ -2647,6 +2665,7 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); } int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture() ; } int Monitor::PostCapture() const { return camera->PostCapture() ; }
int Monitor::Close() { return camera->Close(); };
Monitor::Orientation Monitor::getOrientation() const { return orientation; } Monitor::Orientation Monitor::getOrientation() const { return orientation; }
// Wait for camera to get an image, and then assign it as the base reference image. So this should be done as the first task in the analysis thread startup. // Wait for camera to get an image, and then assign it as the base reference image. So this should be done as the first task in the analysis thread startup.

View File

@ -117,38 +117,38 @@ protected:
uint32_t last_write_index; /* +4 */ uint32_t last_write_index; /* +4 */
uint32_t last_read_index; /* +8 */ uint32_t last_read_index; /* +8 */
uint32_t state; /* +12 */ uint32_t state; /* +12 */
uint32_t last_event_id; /* +16 */ uint64_t last_event_id; /* +16 */
uint32_t action; /* +20 */ uint32_t action; /* +24 */
int32_t brightness; /* +24 */ int32_t brightness; /* +28 */
int32_t hue; /* +28 */ int32_t hue; /* +32 */
int32_t colour; /* +32 */ int32_t colour; /* +36 */
int32_t contrast; /* +36 */ int32_t contrast; /* +40 */
int32_t alarm_x; /* +40 */ int32_t alarm_x; /* +44 */
int32_t alarm_y; /* +44 */ int32_t alarm_y; /* +48 */
uint8_t valid; /* +48 */ uint8_t valid; /* +52 */
uint8_t active; /* +49 */ uint8_t active; /* +53 */
uint8_t signal; /* +50 */ uint8_t signal; /* +54 */
uint8_t format; /* +51 */ uint8_t format; /* +55 */
uint32_t imagesize; /* +52 */ uint32_t imagesize; /* +56 */
uint32_t epadding1; /* +56 */ uint32_t epadding1; /* +60 */
uint32_t epadding2; /* +60 */ uint32_t epadding2; /* +64 */
/* /*
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
*/ */
union { /* +64 */ union { /* +68 */
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */ time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
uint64_t extrapad1; uint64_t extrapad1;
}; };
union { /* +72 */ union { /* +76 */
time_t last_write_time; time_t last_write_time;
uint64_t extrapad2; uint64_t extrapad2;
}; };
union { /* +80 */ union { /* +84 */
time_t last_read_time; time_t last_read_time;
uint64_t extrapad3; uint64_t extrapad3;
}; };
uint8_t control_state[256]; /* +88 */ uint8_t control_state[256]; /* +92 */
char alarm_cause[256]; char alarm_cause[256];
@ -299,13 +299,16 @@ protected:
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image. int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not bool embed_exif; // Whether to embed Exif data into each image frame or not
double capture_fps; // Current capturing fps double capture_fps; // Current capturing fps
double analysis_fps; // Current analysis fps double analysis_fps; // Current analysis fps
unsigned int last_camera_bytes;
Image delta_image; Image delta_image;
Image ref_image; Image ref_image;
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
@ -339,8 +342,6 @@ protected:
#endif // ZM_MEM_MAPPED #endif // ZM_MEM_MAPPED
off_t mem_size; off_t mem_size;
unsigned char *mem_ptr; unsigned char *mem_ptr;
Storage *storage;
SharedData *shared_data; SharedData *shared_data;
TriggerData *trigger_data; TriggerData *trigger_data;
VideoStoreData *video_store_data; VideoStoreData *video_store_data;
@ -353,7 +354,8 @@ protected:
int video_stream_id; // will be filled in PrimeCapture int video_stream_id; // will be filled in PrimeCapture
Camera *camera; Camera *camera;
Event *event; Event *event;
Storage *storage;
int n_zones; int n_zones;
Zone **zones; Zone **zones;
@ -487,6 +489,7 @@ public:
int PreCapture() const; int PreCapture() const;
int Capture(); int Capture();
int PostCapture() const; int PostCapture() const;
int Close();
void CheckAction(); void CheckAction();

View File

@ -21,6 +21,7 @@
#include "zm_rtsp_auth.h" #include "zm_rtsp_auth.h"
#include "zm_mem_utils.h" #include "zm_mem_utils.h"
#include "zm_signal.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -185,24 +186,24 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
struct timeval temp_timeout = timeout; struct timeval temp_timeout = timeout;
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); int n_found = select(sd+1, &rfds, NULL, NULL, &temp_timeout);
if( n_found == 0 ) { if( n_found == 0 ) {
Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec ); Debug( 1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec );
int error = 0; int error = 0;
socklen_t len = sizeof (error); socklen_t len = sizeof (error);
int retval = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len); int retval = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
if(retval != 0 ) { if(retval != 0 ) {
Debug( 1, "error getting socket error code %s", strerror(retval) ); Debug( 1, "error getting socket error code %s", strerror(retval) );
} }
if (error != 0) { if (error != 0 ) {
return -1; return -1;
} }
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available. // Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
//Disconnect(); //Disconnect();
return( 0 ); return 0;
} else if ( n_found < 0) { } else if ( n_found < 0) {
Error( "Select error: %s", strerror(errno) ); Error("Select error: %s", strerror(errno));
return( -1 ); return -1;
} }
unsigned int total_bytes_to_read = 0; unsigned int total_bytes_to_read = 0;
@ -298,13 +299,14 @@ int RemoteCameraHttp::GetResponse() {
static RegExpr *content_length_expr = 0; static RegExpr *content_length_expr = 0;
static RegExpr *content_type_expr = 0; static RegExpr *content_type_expr = 0;
while ( ! ( buffer_len = ReadData( buffer ) ) ) { while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
Debug(4, "Timeout waiting for REGEXP HEADER"); Debug(4, "Timeout waiting for REGEXP HEADER");
} }
if ( buffer_len < 0 ) { if ( buffer_len < 0 ) {
Error( "Unable to read header data" ); Error( "Unable to read header data" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
if ( !header_expr ) if ( !header_expr )
header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL );
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) { if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) {
@ -449,13 +451,14 @@ int RemoteCameraHttp::GetResponse() {
state = CONTENT; state = CONTENT;
} else { } else {
Debug( 3, "Unable to extract subheader from stream, retrying" ); Debug( 3, "Unable to extract subheader from stream, retrying" );
while ( ! ( buffer_len = ReadData( buffer ) ) ) { while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
Debug(4, "Timeout waiting to extract subheader"); Debug(4, "Timeout waiting to extract subheader");
} }
if ( buffer_len < 0 ) { if ( buffer_len < 0 ) {
Error( "Unable to extract subheader data" ); Error( "Unable to extract subheader data" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
} }
break; break;
} }
@ -480,23 +483,27 @@ int RemoteCameraHttp::GetResponse() {
} }
if ( content_length ) { if ( content_length ) {
while ( (long)buffer.size() < content_length ) { while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
if ( ReadData( buffer ) < 0 ) { int bytes_read = ReadData( buffer );
if ( bytes_read < 0 ) {
Error( "Unable to read content" ); Error( "Unable to read content" );
return( -1 ); return( -1 );
} }
bytes += bytes_read;
} }
Debug( 3, "Got end of image by length, content-length = %d", content_length ); Debug( 3, "Got end of image by length, content-length = %d", content_length );
} else { } else {
while ( !content_length ) { while ( !content_length ) {
while ( ! ( buffer_len = ReadData( buffer ) ) ) { while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
Debug(4, "Timeout waiting for content"); Debug(4, "Timeout waiting for content");
} }
if ( buffer_len < 0 ) { if ( buffer_len < 0 ) {
Error( "Unable to read content" ); Error( "Unable to read content" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
static RegExpr *content_expr = 0; static RegExpr *content_expr = 0;
if ( mode == MULTI_IMAGE ) { if ( mode == MULTI_IMAGE ) {
if ( !content_expr ) { if ( !content_expr ) {
@ -603,13 +610,14 @@ int RemoteCameraHttp::GetResponse() {
} }
case HEADERCONT : case HEADERCONT :
{ {
while ( ! ( buffer_len = ReadData( buffer ) ) ) { while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
Debug(4, "Timeout waiting for HEADERCONT"); Debug(4, "Timeout waiting for HEADERCONT");
} }
if ( buffer_len < 0 ) { if ( buffer_len < 0 ) {
Error( "Unable to read header" ); Error( "Unable to read header" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
char *crlf = 0; char *crlf = 0;
char *header_ptr = (char *)buffer; char *header_ptr = (char *)buffer;
@ -888,13 +896,14 @@ int RemoteCameraHttp::GetResponse() {
state = CONTENT; state = CONTENT;
} else { } else {
Debug( 3, "Unable to extract subheader from stream, retrying" ); Debug( 3, "Unable to extract subheader from stream, retrying" );
while ( ! ( buffer_len = ReadData( buffer ) ) ) { while ( !( buffer_len = ReadData(buffer) ) &&!zm_terminate ) {
Debug(1, "Timeout waiting to extra subheader non regexp"); Debug(1, "Timeout waiting to extra subheader non regexp");
} }
if ( buffer_len < 0 ) { if ( buffer_len < 0 ) {
Error( "Unable to read subheader" ); Error( "Unable to read subheader" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
state = SUBHEADERCONT; state = SUBHEADERCONT;
} }
break; break;
@ -927,18 +936,19 @@ int RemoteCameraHttp::GetResponse() {
} }
if ( content_length ) { if ( content_length ) {
while ( (long)buffer.size() < content_length ) { while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
//int buffer_len = ReadData( buffer, content_length-buffer.size() );
Debug(4, "getting more data"); Debug(4, "getting more data");
if ( ReadData( buffer ) < 0 ) { int bytes_read = ReadData(buffer);
Error( "Unable to read content" ); if ( bytes_read < 0 ) {
return( -1 ); Error("Unable to read content");
return -1;
} }
bytes += bytes_read;
} }
Debug( 3, "Got end of image by length, content-length = %d", content_length ); Debug( 3, "Got end of image by length, content-length = %d", content_length );
} else { } else {
// Read until we find the end of image or the stream closes. // Read until we find the end of image or the stream closes.
while ( !content_length ) { while ( !content_length && !zm_terminate ) {
Debug(4, "!content_length, ReadData"); Debug(4, "!content_length, ReadData");
buffer_len = ReadData( buffer ); buffer_len = ReadData( buffer );
if ( buffer_len < 0 ) if ( buffer_len < 0 )
@ -946,6 +956,7 @@ int RemoteCameraHttp::GetResponse() {
Error( "Unable to read content" ); Error( "Unable to read content" );
return( -1 ); return( -1 );
} }
bytes += buffer_len;
int buffer_size = buffer.size(); int buffer_size = buffer.size();
if ( buffer_len ) { if ( buffer_len ) {
// Got some data // Got some data

View File

@ -72,7 +72,8 @@ public:
int PreCapture(); int PreCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
AVStream* get_VideoStream(); AVStream* get_VideoStream();
int Close() { return 0; };
}; };
#endif // ZM_REMOTE_CAMERA_HTTP_H #endif // ZM_REMOTE_CAMERA_HTTP_H

View File

@ -189,13 +189,14 @@ int RemoteCameraNVSocket::Capture( ZMPacket &zm_packet ) {
Warning( "Unable to capture image, retrying" ); Warning( "Unable to capture image, retrying" );
return 0; return 0;
} }
if ( Read( sd, buffer, imagesize ) < imagesize ) { int bytes_read = Read(sd, buffer, imagesize);
Warning( "Unable to capture image, retrying" ); if ( (bytes_read < 0) || ( (unsigned int)bytes_read < imagesize ) ) {
Warning("Unable to capture image, retrying");
return 0; return 0;
} }
uint32_t end; uint32_t end;
if ( Read(sd, (char *) &end , sizeof(end)) < 0 ) { if ( Read(sd, (char *) &end , sizeof(end)) < 0 ) {
Warning( "Unable to capture image, retrying" ); Warning("Unable to capture image, retrying");
return 0; return 0;
} }
if ( end != 0xFFFFFFFF) { if ( end != 0xFFFFFFFF) {
@ -203,7 +204,7 @@ int RemoteCameraNVSocket::Capture( ZMPacket &zm_packet ) {
return 0; return 0;
} }
zm_packet.image->Assign( width, height, colours, subpixelorder, buffer, imagesize ); zm_packet.image->Assign(width, height, colours, subpixelorder, buffer, imagesize);
zm_packet.keyframe = 1; zm_packet.keyframe = 1;
return 1; return 1;
} }

View File

@ -65,7 +65,8 @@ public:
int PrimeCapture(); int PrimeCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
AVStream* get_VideoStream(); AVStream* get_VideoStream();
int Close() { return 0; };
}; };
#endif // ZM_REMOTE_CAMERA_NVSOCKET_H #endif // ZM_REMOTE_CAMERA_NVSOCKET_H

View File

@ -85,6 +85,7 @@ public:
int PreCapture(); int PreCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
int Close() { return 0; };
}; };
#endif // ZM_REMOTE_CAMERA_RTSP_H #endif // ZM_REMOTE_CAMERA_RTSP_H

View File

@ -20,7 +20,7 @@
#define __STDC_FORMAT_MACROS 1 #define __STDC_FORMAT_MACROS 1
#include <inttypes.h> #include <cinttypes>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>

View File

@ -108,14 +108,12 @@ void Zone::Setup(
} }
} }
// FIXME: Is this not a problem? If you had two zones for a monitor.. then these would conflict. You would only get 1 dump file
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; snprintf(diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
if ( ! diag_path[0] ) { pg_image->WriteJpeg(diag_path);
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id); } else {
} diag_path[0] = 0;
pg_image->WriteJpeg( diag_path ); }
}
} // end Zone::Setup } // end Zone::Setup
Zone::~Zone() { Zone::~Zone() {
@ -127,21 +125,22 @@ Zone::~Zone() {
void Zone::RecordStats( const Event *event ) { void Zone::RecordStats( const Event *event ) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score );
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { snprintf(sql, sizeof(sql),
Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); "INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d",
monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score
);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert event stats: %s", mysql_error(&dbconn));
} }
db_mutex.unlock(); db_mutex.unlock();
} // end void Zone::RecordStats( const Event *event ) } // end void Zone::RecordStats( const Event *event )
bool Zone::CheckOverloadCount() { bool Zone::CheckOverloadCount() {
Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames);
if ( overload_count ) { if ( overload_count ) {
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); Debug(4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames);
Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--; overload_count--;
return( false ); return false;
} }
return true; return true;
} // end bool Zone::CheckOverloadCount() } // end bool Zone::CheckOverloadCount()
@ -180,28 +179,27 @@ int Zone::GetExtendAlarmFrames() {
} // end int Zone::GetExtendAlarmFrames() } // end int Zone::GetExtendAlarmFrames()
bool Zone::CheckExtendAlarmCount() { bool Zone::CheckExtendAlarmCount() {
Info( "ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames ); Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames);
if ( extend_alarm_count ) { if ( extend_alarm_count ) {
Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames ); Debug(3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames);
extend_alarm_count--; extend_alarm_count--;
return( true ); return true;
} }
return false; return false;
} // end bool Zone::CheckExtendAlarmCount } // end bool Zone::CheckExtendAlarmCount
bool Zone::CheckAlarms( const Image *delta_image ) { bool Zone::CheckAlarms(const Image *delta_image) {
ResetStats(); ResetStats();
if ( overload_count ) { if ( overload_count ) {
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); Info("In overload mode, %d frames of %d remaining", overload_count, overload_frames);
Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--; overload_count--;
return( false ); return false;
} }
delete image; delete image;
// Get the difference image // Get the difference image
Image *diff_image = image = new Image( *delta_image ); Image *diff_image = image = new Image(*delta_image);
int diff_width = diff_image->Width(); int diff_width = diff_image->Width();
uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); uint8_t* diff_buff = (uint8_t*)diff_image->Buffer();
uint8_t* pdiff; uint8_t* pdiff;
@ -221,7 +219,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
unsigned int hi_x = polygon.HiX(); unsigned int hi_x = polygon.HiX();
unsigned int hi_y = polygon.HiY(); unsigned int hi_y = polygon.HiY();
Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); Debug(4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y);
/* if(config.cpu_extensions && sseversion >= 20) { /* if(config.cpu_extensions && sseversion >= 20) {
sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
@ -230,36 +228,31 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
} */ } */
std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
if ( config.record_diag_images ) { if ( config.record_diag_images )
static char diag_path[PATH_MAX] = ""; diff_image->WriteJpeg(diag_path);
if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 1 );
}
diff_image->WriteJpeg( diag_path );
}
if ( pixel_diff_count && alarm_pixels ) if ( pixel_diff_count && alarm_pixels )
pixel_diff = pixel_diff_count/alarm_pixels; pixel_diff = pixel_diff_count/alarm_pixels;
Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
if( alarm_pixels ) { if ( alarm_pixels ) {
if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
/* Not enough pixels alarmed */ /* Not enough pixels alarmed */
return (false); return false;
} else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { } else if ( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) {
/* Too many pixels alarmed */ /* Too many pixels alarmed */
overload_count = overload_frames; overload_count = overload_frames;
return (false); return false;
} }
} else { } else {
/* No alarmed pixels */ /* No alarmed pixels */
return (false); return false;
} }
score = (100*alarm_pixels)/polygon.Area(); score = (100*alarm_pixels)/polygon.Area();
if ( score < 1 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score ); Debug(5, "Current score is %d", score);
if ( check_method >= FILTERED_PIXELS ) { if ( check_method >= FILTERED_PIXELS ) {
int bx = filter_box.X(); int bx = filter_box.X();
@ -268,7 +261,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
int by1 = by-1; int by1 = by-1;
Debug( 5, "Checking for filtered pixels" ); Debug(5, "Checking for filtered pixels");
if ( bx > 1 || by > 1 ) { if ( bx > 1 || by > 1 ) {
// Now remove any pixels smaller than our filter size // Now remove any pixels smaller than our filter size
unsigned char *cpdiff; unsigned char *cpdiff;
@ -306,47 +299,42 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
continue; continue;
} }
alarm_filter_pixels++; alarm_filter_pixels++;
} } // end if white
} } // end for x
} } // end foreach y line
} else { } else {
alarm_filter_pixels = alarm_pixels; alarm_filter_pixels = alarm_pixels;
} }
if ( config.record_diag_images ) { if ( config.record_diag_images )
static char diag_path[PATH_MAX] = ""; diff_image->WriteJpeg(diag_path);
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 2 );
}
diff_image->WriteJpeg( diag_path );
}
Debug( 5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels ); Debug(5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
if( alarm_filter_pixels ) { if ( alarm_filter_pixels ) {
if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
/* Not enough pixels alarmed */ /* Not enough pixels alarmed */
return (false); return false;
} else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { } else if ( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) {
/* Too many pixels alarmed */ /* Too many pixels alarmed */
overload_count = overload_frames; overload_count = overload_frames;
return (false); return false;
} }
} else { } else {
/* No filtered pixels */ /* No filtered pixels */
return (false); return false;
} }
score = (100*alarm_filter_pixels)/(polygon.Area()); score = (100*alarm_filter_pixels)/(polygon.Area());
if ( score < 1 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score ); Debug(5, "Current score is %d", score);
if ( check_method >= BLOBS ) { if ( check_method >= BLOBS ) {
Debug( 5, "Checking for blob pixels" ); Debug(5, "Checking for blob pixels");
typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats;
BlobStats blob_stats[256]; BlobStats blob_stats[256];
memset( blob_stats, 0, sizeof(BlobStats)*256 ); memset(blob_stats, 0, sizeof(BlobStats)*256);
uint8_t *spdiff; uint8_t *spdiff;
uint8_t last_x, last_y; uint8_t last_x, last_y;
BlobStats *bsx, *bsy; BlobStats *bsx, *bsy;
@ -358,32 +346,32 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); pdiff = (uint8_t*)diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) {
if ( *pdiff == WHITE ) { if ( *pdiff == WHITE ) {
Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff ); Debug(9, "Got white pixel at %d,%d (%p)", x, y, pdiff);
//last_x = (x>lo_x)?*(pdiff-1):0; //last_x = (x>lo_x)?*(pdiff-1):0;
//last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0;
last_x = 0; last_x = 0;
if(x > 0) { if ( x > 0 ) {
if((x-1) >= lo_x) { if ( (x-1) >= lo_x ) {
last_x = *(pdiff-1); last_x = *(pdiff-1);
} }
} }
last_y = 0; last_y = 0;
if(y > 0) { if (y > 0 ) {
if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) { if ( (y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x ) {
last_y = *(pdiff-diff_width); last_y = *(pdiff-diff_width);
} }
} }
if ( last_x ) { if ( last_x ) {
Debug( 9, "Left neighbour is %d", last_x ); Debug(9, "Left neighbour is %d", last_x);
bsx = &blob_stats[last_x]; bsx = &blob_stats[last_x];
if ( last_y ) { if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y ); Debug(9, "Top neighbour is %d", last_y);
bsy = &blob_stats[last_y]; bsy = &blob_stats[last_y];
if ( last_x == last_y ) { if ( last_x == last_y ) {
Debug( 9, "Matching neighbours, setting to %d", last_x ); Debug(9, "Matching neighbours, setting to %d", last_x);
// Add to the blob from the x side (either side really) // Add to the blob from the x side (either side really)
*pdiff = last_x; *pdiff = last_x;
alarm_blob_pixels++; alarm_blob_pixels++;
@ -395,22 +383,29 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bsm = bsx->count>=bsy->count?bsx:bsy; bsm = bsx->count>=bsy->count?bsx:bsy;
bss = bsm==bsx?bsy:bsx; bss = bsm==bsx?bsy:bsx;
Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag ); Debug(9,
Debug( 9, "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); "Different neighbours, setting pixels of %d to %d\n"
Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d\n"
"Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d\n",
bss->tag, bsm->tag,
bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y,
bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y
);
// Now change all those pixels to the other setting // Now change all those pixels to the other setting
int changed = 0; int changed = 0;
for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) { for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++ ) {
int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x; int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x;
int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x; int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x;
Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx ); Debug(9,
Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x ); "Changing %d, %d->%d Range %d->%d",
sy, lo_sx, hi_sx, ranges[sy].lo_x, ranges[sy].hi_x
);
spdiff = diff_buff + ((diff_width * sy) + lo_sx); spdiff = diff_buff + ((diff_width * sy) + lo_sx);
for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) { for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) {
Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff ); Debug(9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff);
if ( *spdiff == bss->tag ) { if ( *spdiff == bss->tag ) {
Debug( 9, "Setting pixel" ); Debug(9, "Setting pixel");
*spdiff = bsm->tag; *spdiff = bsm->tag;
changed++; changed++;
} }
@ -419,10 +414,14 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
*pdiff = bsm->tag; *pdiff = bsm->tag;
alarm_blob_pixels++; alarm_blob_pixels++;
if ( !changed ) { if ( !changed ) {
Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); Info(
Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d\n"
Error( "No pixels changed, exiting" ); "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d",
exit( -1 ); bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y,
bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y
);
Error("No pixels changed, exiting");
exit(-1);
} }
// Merge the slave blob into the master // Merge the slave blob into the master
@ -436,7 +435,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
alarm_blobs--; alarm_blobs--;
Debug( 6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs ); Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs);
// Clear out the old blob // Clear out the old blob
bss->tag = 0; bss->tag = 0;
@ -447,7 +446,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bss->hi_y = 0; bss->hi_y = 0;
} }
} else { } else {
Debug( 9, "Setting to left neighbour %d", last_x ); Debug(9, "Setting to left neighbour %d", last_x);
// Add to the blob from the x side // Add to the blob from the x side
*pdiff = last_x; *pdiff = last_x;
alarm_blob_pixels++; alarm_blob_pixels++;
@ -457,8 +456,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
} }
} else { } else {
if ( last_y ) { if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y ); Debug(9, "Top neighbour is %d", last_y);
Debug( 9, "Setting to top neighbour %d", last_y );
// Add to the blob from the y side // Add to the blob from the y side
BlobStats *bsy = &blob_stats[last_y]; BlobStats *bsy = &blob_stats[last_y];
@ -489,7 +487,8 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
alarm_blobs--; alarm_blobs--;
alarm_blob_pixels -= bs->count; alarm_blob_pixels -= bs->count;
Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); Debug(6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs",
i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs);
bs->tag = 0; bs->tag = 0;
bs->count = 0; bs->count = 0;
@ -500,7 +499,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
} }
} }
if ( !bs->count ) { if ( !bs->count ) {
Debug( 9, "Creating new blob %d", i ); Debug(9, "Creating new blob %d", i);
*pdiff = i; *pdiff = i;
alarm_blob_pixels++; alarm_blob_pixels++;
bs->tag = i; bs->tag = i;
@ -509,12 +508,12 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bs->lo_y = bs->hi_y = y; bs->lo_y = bs->hi_y = y;
alarm_blobs++; alarm_blobs++;
Debug( 6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs ); Debug(6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs);
break; break;
} }
} }
if ( i == 0 ) { if ( i == 0 ) {
Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." ); Warning("Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive.");
x = hi_x+1; x = hi_x+1;
y = hi_y+1; y = hi_y+1;
} }
@ -523,19 +522,15 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
} }
} }
} }
if ( config.record_diag_images ) { if ( config.record_diag_images )
static char diag_path[PATH_MAX] = ""; diff_image->WriteJpeg(diag_path);
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 3 );
}
diff_image->WriteJpeg( diag_path );
}
if ( !alarm_blobs ) { if ( !alarm_blobs ) {
return( false ); return false;
} }
Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); Debug(5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d",
alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs);
// Now eliminate blobs under the threshold // Now eliminate blobs under the threshold
for ( int i = 1; i < WHITE; i++ ) { for ( int i = 1; i < WHITE; i++ ) {
@ -555,7 +550,8 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
alarm_blobs--; alarm_blobs--;
alarm_blob_pixels -= bs->count; alarm_blob_pixels -= bs->count;
Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); Debug(6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs",
i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs );
bs->tag = 0; bs->tag = 0;
bs->count = 0; bs->count = 0;
@ -564,39 +560,37 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bs->hi_x = 0; bs->hi_x = 0;
bs->hi_y = 0; bs->hi_y = 0;
} else { } else {
Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); Debug(6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs",
i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs );
if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count; if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count;
if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count; if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count;
} }
} } // end if bs_count
} } // end for i < WHITE
if ( config.record_diag_images ) { if ( config.record_diag_images )
static char diag_path[PATH_MAX] = ""; diff_image->WriteJpeg(diag_path);
if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 4 );
}
diff_image->WriteJpeg( diag_path );
}
Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs );
if( alarm_blobs ) { Debug(5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d",
if( min_blobs && (alarm_blobs < min_blobs) ) { alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs);
if ( alarm_blobs ) {
if ( min_blobs && (alarm_blobs < min_blobs) ) {
/* Not enough pixels alarmed */ /* Not enough pixels alarmed */
return (false); return false;
} else if(max_blobs && (alarm_blobs > max_blobs) ) { } else if ( max_blobs && (alarm_blobs > max_blobs) ) {
/* Too many pixels alarmed */ /* Too many pixels alarmed */
overload_count = overload_frames; overload_count = overload_frames;
return (false); return false;
} }
} else { } else {
/* No blobs */ /* No blobs */
return (false); return false;
} }
score = (100*alarm_blob_pixels)/(polygon.Area()); score = (100*alarm_blob_pixels)/(polygon.Area());
if ( score < 1 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug( 5, "Current score is %d", score ); Debug(5, "Current score is %d", score);
alarm_lo_x = polygon.HiX()+1; alarm_lo_x = polygon.HiX()+1;
alarm_hi_x = polygon.LoX()-1; alarm_hi_x = polygon.LoX()-1;
@ -648,15 +642,15 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
score *= 2; score *= 2;
} }
Debug( 5, "Adjusted score is %d", score ); Debug(5, "Adjusted score is %d", score);
// Now outline the changed region // Now outline the changed region
if ( score ) { if ( score ) {
alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) ); alarm_box = Box(Coord(alarm_lo_x, alarm_lo_y), Coord(alarm_hi_x, alarm_hi_y));
//if ( monitor->followMotion() ) //if ( monitor->followMotion() )
if ( true ) { if ( true ) {
alarm_centre = Coord( alarm_mid_x, alarm_mid_y ); alarm_centre = Coord(alarm_mid_x, alarm_mid_y);
} else { } else {
alarm_centre = alarm_box.Centre(); alarm_centre = alarm_box.Centre();
} }
@ -698,9 +692,9 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
} }
if ( monitor->Colours() == ZM_COLOUR_GRAY8 ) { if ( monitor->Colours() == ZM_COLOUR_GRAY8 ) {
image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); image = diff_image->HighlightEdges(alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent());
} else { } else {
image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); image = diff_image->HighlightEdges(alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent());
} }
// Only need to delete this when 'image' becomes detached and points somewhere else // Only need to delete this when 'image' becomes detached and points somewhere else
@ -710,18 +704,18 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
image = 0; image = 0;
} }
Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Debug(1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d",
Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score);
} }
return( true ); return true;
} }
bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) { bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) {
Debug( 3, "Parsing polygon string '%s'", poly_string ); Debug(3, "Parsing polygon string '%s'", poly_string);
char *str_ptr = new char[strlen(poly_string)+1]; char *str_ptr = new char[strlen(poly_string)+1];
char *str = str_ptr; char *str = str_ptr;
strcpy( str, poly_string ); strcpy(str, poly_string);
char *ws; char *ws;
int n_coords = 0; int n_coords = 0;
@ -731,16 +725,16 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
if ( *str == '\0' ) { if ( *str == '\0' ) {
break; break;
} }
ws = strchr( str, ' ' ); ws = strchr(str, ' ');
if ( ws ) { if ( ws ) {
*ws = '\0'; *ws = '\0';
} }
char *cp = strchr( str, ',' ); char *cp = strchr(str, ',');
if ( !cp ) { if ( !cp ) {
Error( "Bogus coordinate %s found in polygon string", str ); Error("Bogus coordinate %s found in polygon string", str);
delete[] coords; delete[] coords;
delete[] str_ptr; delete[] str_ptr;
return( false ); return false;
} else { } else {
*cp = '\0'; *cp = '\0';
char *xp = str; char *xp = str;
@ -749,7 +743,7 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
int x = atoi(xp); int x = atoi(xp);
int y = atoi(yp); int y = atoi(yp);
Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); Debug(3, "Got coordinate %d,%d from polygon string", x, y);
#if 0 #if 0
if ( x < 0 ) if ( x < 0 )
x = 0; x = 0;
@ -767,82 +761,83 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
else else
break; break;
} }
polygon = Polygon( n_coords, coords ); polygon = Polygon(n_coords, coords);
Debug( 3, "Successfully parsed polygon string" ); Debug(3, "Successfully parsed polygon string");
//printf( "Area: %d\n", pg.Area() ); //printf( "Area: %d\n", pg.Area() );
//printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() );
delete[] coords; delete[] coords;
delete[] str_ptr; delete[] str_ptr;
return( true ); return true;
} }
bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) { bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) {
Debug( 3, "Parsing zone string '%s'", zone_string ); Debug(3, "Parsing zone string '%s'", zone_string);
char *str_ptr = new char[strlen(zone_string)+1]; char *str_ptr = new char[strlen(zone_string)+1];
char *str = str_ptr; char *str = str_ptr;
strcpy( str, zone_string ); strcpy(str, zone_string);
char *ws = strchr( str, ' ' ); char *ws = strchr(str, ' ');
if ( !ws ) { if ( !ws ) {
Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); Debug(3, "No initial whitespace found in zone string '%s', finishing", str);
} }
zone_id = strtol( str, 0, 10 ); zone_id = strtol(str, 0, 10);
Debug( 3, "Got zone %d from zone string", zone_id ); Debug(3, "Got zone %d from zone string", zone_id);
if ( !ws ) { if ( !ws ) {
delete[] str_ptr; delete[] str_ptr;
return( true ); return true;
} }
*ws = '\0'; *ws = '\0';
str = ws+1; str = ws+1;
ws = strchr( str, ' ' ); ws = strchr(str, ' ');
if ( !ws ) { if ( !ws ) {
Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); Debug(3, "No secondary whitespace found in zone string '%s', finishing", zone_string);
} }
colour = strtol( str, 0, 16 ); colour = strtol(str, 0, 16);
Debug( 3, "Got colour %06x from zone string", colour ); Debug(3, "Got colour %06x from zone string", colour);
if ( !ws ) { if ( !ws ) {
delete[] str_ptr; delete[] str_ptr;
return( true ); return true;
} }
*ws = '\0'; *ws = '\0';
str = ws+1; str = ws+1;
bool result = ParsePolygonString( str, polygon ); bool result = ParsePolygonString(str, polygon);
//printf( "Area: %d\n", pg.Area() ); //printf( "Area: %d\n", pg.Area() );
//printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() );
delete[] str_ptr; delete[] str_ptr;
return( result ); return result;
} }
int Zone::Load( Monitor *monitor, Zone **&zones ) { int Zone::Load(Monitor *monitor, Zone **&zones) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() );
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { snprintf(sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id());
Error( "Can't run query: %s", mysql_error( &dbconn ) ); if ( mysql_query(&dbconn, sql) ) {
exit( mysql_errno( &dbconn ) ); Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock();
return 0;
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
db_mutex.unlock(); db_mutex.unlock();
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error("Can't use query result: %s", mysql_error(&dbconn));
return 0; return 0;
} }
int n_zones = mysql_num_rows( result ); int n_zones = mysql_num_rows(result);
Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); Debug(1, "Got %d zones for monitor %s", n_zones, monitor->Name());
delete[] zones; delete[] zones;
zones = new Zone *[n_zones]; zones = new Zone *[n_zones];
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
int col = 0; int col = 0;
int Id = atoi(dbrow[col++]); int Id = atoi(dbrow[col++]);
@ -870,17 +865,18 @@ int Zone::Load( Monitor *monitor, Zone **&zones ) {
/* HTML colour code is actually BGR in memory, we want RGB */ /* HTML colour code is actually BGR in memory, we want RGB */
AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR);
Debug( 5, "Parsing polygon %s", Coords ); Debug(5, "Parsing polygon %s", Coords);
Polygon polygon; Polygon polygon;
if ( !ParsePolygonString( Coords, polygon ) ) { if ( !ParsePolygonString(Coords, polygon) ) {
Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); Error("Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name());
n_zones -= 1; n_zones -= 1;
continue; continue;
} }
if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width()
|| polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) {
Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring",
Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY());
n_zones -= 1; n_zones -= 1;
continue; continue;
} }
@ -895,21 +891,17 @@ int Zone::Load( Monitor *monitor, Zone **&zones ) {
} }
if ( atoi(dbrow[2]) == Zone::INACTIVE ) { if ( atoi(dbrow[2]) == Zone::INACTIVE ) {
zones[i] = new Zone( monitor, Id, Name, polygon ); zones[i] = new Zone(monitor, Id, Name, polygon);
} else if ( atoi(dbrow[2]) == Zone::PRIVACY ) { } else if ( atoi(dbrow[2]) == Zone::PRIVACY ) {
zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon ); zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon);
} }
zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames ); zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames);
} } // end foreach row
if ( mysql_errno( &dbconn ) ) { mysql_free_result(result);
Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); return n_zones;
exit( mysql_errno( &dbconn ) ); } // end int Zone::Load(Monitor *monitor, Zone **&zones)
}
mysql_free_result( result );
return( n_zones );
}
bool Zone::DumpSettings( char *output, bool /*verbose*/ ) { bool Zone::DumpSettings(char *output, bool /*verbose*/) {
output[0] = 0; output[0] = 0;
sprintf( output+strlen(output), " Id : %d\n", id ); sprintf( output+strlen(output), " Id : %d\n", id );
@ -953,7 +945,7 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
unsigned int lo_y; unsigned int lo_y;
unsigned int hi_y; unsigned int hi_y;
if(max_pixel_threshold) if ( max_pixel_threshold )
calc_max_pixel_threshold = max_pixel_threshold; calc_max_pixel_threshold = max_pixel_threshold;
lo_y = polygon.LoY(); lo_y = polygon.LoY();
@ -962,9 +954,9 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
unsigned int lo_x = ranges[y].lo_x; unsigned int lo_x = ranges[y].lo_x;
unsigned int hi_x = ranges[y].hi_x; unsigned int hi_x = ranges[y].hi_x;
Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); Debug(7, "Checking line %d from %d -> %d", y, lo_x, hi_x);
uint8_t *pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); uint8_t *pdiff = (uint8_t*)pdiff_image->Buffer(lo_x, y);
const uint8_t *ppoly = ppoly_image->Buffer( lo_x, y ); const uint8_t *ppoly = ppoly_image->Buffer(lo_x, y);
for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) { for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) {
if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) { if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) {

View File

@ -94,6 +94,7 @@ protected:
int overload_count; int overload_count;
int extend_alarm_count; int extend_alarm_count;
char diag_path[PATH_MAX];
protected: protected:
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ); void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );

View File

@ -146,7 +146,7 @@ int main( int argc, char *argv[] ) {
monitor->UpdateAdaptiveSkip(); monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time( 0 ); last_analysis_update_time = time( 0 );
while( !zm_terminate ) { while( (!zm_terminate) && monitor->ShmValid() ) {
// Process the next image // Process the next image
sigprocmask(SIG_BLOCK, &block_set, 0); sigprocmask(SIG_BLOCK, &block_set, 0);

View File

@ -1,21 +1,21 @@
// //
// ZoneMinder Capture Daemon, $Date$, $Revision$ // ZoneMinder Capture Daemon, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes // Copyright (C) 2001-2008 Philip Coombes
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License // modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
/* /*
@ -39,7 +39,7 @@ zmc - The ZoneMinder Capture daemon
=head1 DESCRIPTION =head1 DESCRIPTION
This binary's job is to sit on a video device and suck frames off it as fast as This binary's job is to sit on a video device and suck frames off it as fast as
possible, this should run at more or less constant speed. possible, this should run at more or less constant speed.
=head1 OPTIONS =head1 OPTIONS
@ -149,7 +149,7 @@ int main(int argc, char *argv[]) {
std::cout << ZM_VERSION << "\n"; std::cout << ZM_VERSION << "\n";
exit(0); exit(0);
default: default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); // fprintf(stderr, "?? getopt returned character code 0%o ??\n", c);
break; break;
} }
} }
@ -162,7 +162,7 @@ int main(int argc, char *argv[]) {
Usage(); Usage();
} }
int modes = ( (device[0]?1:0) + (host[0]?1:0) + (file[0]?1:0) + (monitor_id > 0 ? 1 : 0)); int modes = ( (device[0]?1:0) + (host[0]?1:0) + (file[0]?1:0) + (monitor_id > 0 ? 1 : 0) );
if ( modes > 1 ) { if ( modes > 1 ) {
fprintf(stderr, "Only one of device, host/port/path, file or monitor id allowed\n"); fprintf(stderr, "Only one of device, host/port/path, file or monitor id allowed\n");
Usage(); Usage();
@ -200,7 +200,7 @@ int main(int argc, char *argv[]) {
if ( device[0] ) { if ( device[0] ) {
n_monitors = Monitor::LoadLocalMonitors(device, monitors, Monitor::CAPTURE); n_monitors = Monitor::LoadLocalMonitors(device, monitors, Monitor::CAPTURE);
} else } else
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
if ( host[0] ) { if ( host[0] ) {
if ( !port ) if ( !port )
port = "80"; port = "80";
@ -222,26 +222,31 @@ int main(int argc, char *argv[]) {
} }
Info("Starting Capture version %s", ZM_VERSION); Info("Starting Capture version %s", ZM_VERSION);
zmSetDefaultHupHandler();
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
sigaddset(&block_set, SIGHUP);
sigaddset(&block_set, SIGUSR1); sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2); sigaddset(&block_set, SIGUSR2);
int result = 0; int result = 0;
while( ! zm_terminate ) {
while ( !zm_terminate ) {
result = 0; result = 0;
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
for ( int i = 0; i < n_monitors; i ++ ) { for ( int i = 0; i < n_monitors; i++ ) {
time_t now = (time_t)time(NULL); time_t now = (time_t)time(NULL);
monitors[i]->setStartupTime(now); monitors[i]->setStartupTime(now);
snprintf(sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id()); snprintf(sql, sizeof(sql),
"REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')",
monitors[i]->Id());
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error("Can't run query: %s", mysql_error(&dbconn));
} }
} }
// Outer primary loop, handles connection to camera // Outer primary loop, handles connection to camera
@ -250,16 +255,19 @@ int main(int argc, char *argv[]) {
sleep(10); sleep(10);
continue; continue;
} }
for ( int i = 0; i < n_monitors; i ++ ) { for ( int i = 0; i < n_monitors; i++ ) {
snprintf(sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id()); snprintf(sql, sizeof(sql),
"REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')",
monitors[i]->Id());
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
} }
} }
AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors]; AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors];
long *capture_delays = new long[n_monitors]; int *capture_delays = new int[n_monitors];
long *alarm_capture_delays = new long[n_monitors]; int *alarm_capture_delays = new int[n_monitors];
int *next_delays = new int[n_monitors];
struct timeval * last_capture_times = new struct timeval[n_monitors]; struct timeval * last_capture_times = new struct timeval[n_monitors];
for ( int i = 0; i < n_monitors; i++ ) { for ( int i = 0; i < n_monitors; i++ ) {
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
@ -288,16 +296,19 @@ int main(int argc, char *argv[]) {
if ( monitors[i]->PreCapture() < 0 ) { if ( monitors[i]->PreCapture() < 0 ) {
Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->Capture() < 0 ) { if ( monitors[i]->Capture() < 0 ) {
Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->PostCapture() < 0 ) { if ( monitors[i]->PostCapture() < 0 ) {
Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1; result = -1;
break; break;
} }
@ -305,9 +316,9 @@ int main(int argc, char *argv[]) {
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
// capture_delay is the amount of time we should sleep to achieve the desired framerate. // capture_delay is the amount of time we should sleep to achieve the desired framerate.
if ( last_capture_times[i].tv_sec ) { if ( last_capture_times[i].tv_sec ) {
long sleep_time; int sleep_time;
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3);
long delay = monitors[i]->GetState() == Monitor::ALARM ? alarm_capture_delays[i] : capture_delays[i]; int delay = monitors[i]->GetState() == Monitor::ALARM ? alarm_capture_delays[i] : capture_delays[i];
sleep_time = delay - delta_time.delta; sleep_time = delay - delta_time.delta;
Debug(3, "Sleep time is %d from now:%d.%d last:%d.%d delay: %d", sleep_time, now.tv_sec, now.tv_usec, last_capture_times[i].tv_sec, last_capture_times[i].tv_usec, delay ); Debug(3, "Sleep time is %d from now:%d.%d last:%d.%d delay: %d", sleep_time, now.tv_sec, now.tv_usec, last_capture_times[i].tv_sec, last_capture_times[i].tv_usec, delay );
@ -334,7 +345,7 @@ int main(int argc, char *argv[]) {
monitors[i]->Reload(); monitors[i]->Reload();
} }
logTerm(); logTerm();
logInit( log_id_string ); logInit(log_id_string);
zm_reload = false; zm_reload = false;
} // end if zm_reload } // end if zm_reload
} // end while ! zm_terminate and connected } // end while ! zm_terminate and connected
@ -353,16 +364,16 @@ int main(int argc, char *argv[]) {
} }
} // end foreach monitor } // end foreach monitor
delete [] analysis_threads; delete [] analysis_threads;
if ( !zm_terminate )
sleep(10);
} // end while ! zm_terminate outer connection loop } // end while ! zm_terminate outer connection loop
Debug(1,"Updating Monitor status"); Debug(1,"Updating Monitor status");
for ( int i = 0; i < n_monitors; i++ ) { for ( int i = 0; i < n_monitors; i++ ) {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')", monitors[i]->Id() ); snprintf(sql, sizeof(sql),
if ( mysql_query( &dbconn, sql ) ) { "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')",
Error( "Can't run query: %s", mysql_error( &dbconn ) ); monitors[i]->Id());
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
} }
delete monitors[i]; delete monitors[i];
} }

View File

@ -19,6 +19,7 @@
#include <sys/ipc.h> #include <sys/ipc.h>
#include <sys/msg.h> #include <sys/msg.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -58,7 +59,7 @@ int main( int argc, const char *argv[] ) {
char format[32] = ""; char format[32] = "";
int monitor_id = 0; int monitor_id = 0;
time_t event_time = 0; time_t event_time = 0;
unsigned long long event_id = 0; uint64_t event_id = 0;
unsigned int frame_id = 1; unsigned int frame_id = 1;
unsigned int scale = 100; unsigned int scale = 100;
unsigned int rate = 100; unsigned int rate = 100;
@ -174,7 +175,7 @@ int main( int argc, const char *argv[] ) {
if ( monitor_id ) { if ( monitor_id ) {
snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id); snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id);
} else { } else {
snprintf(log_id_string, sizeof(log_id_string), "zms_e%llu", event_id); snprintf(log_id_string, sizeof(log_id_string), "zms_e%" PRIu64, event_id);
} }
logInit( log_id_string ); logInit( log_id_string );

View File

@ -87,6 +87,7 @@ Options for use with monitors:
*/ */
#include <getopt.h> #include <getopt.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -95,49 +96,51 @@ Options for use with monitors:
#include "zm_monitor.h" #include "zm_monitor.h"
#include "zm_local_camera.h" #include "zm_local_camera.h"
void Usage( int status=-1 ) { void Usage(int status=-1) {
fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U<username> -P<password>]\n" ); fputs(
fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U<username> -P<password>]\n" ); "zmu <-d device_path> [-v] [function] [-U<username> -P<password>]\n"
fprintf( stderr, "General options:\n" ); "zmu <-m monitor_id> [-v] [function] [-U<username> -P<password>]\n"
fprintf( stderr, " -h, --help : This screen\n" ); "General options:\n"
fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); " -h, --help : This screen\n"
fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); " -v, --verbose : Produce more verbose output\n"
fprintf( stderr, "Options for use with devices:\n" ); " -l, --list : List the current status of active (or all with -v) monitors\n"
fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); "Options for use with devices:\n"
fprintf( stderr, " -V, --version <V4L version> : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n"
fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); " -V, --version <V4L version> : Set the Video 4 Linux API version to use for the query, use 1 or 2\n"
fprintf( stderr, "Options for use with monitors:\n" ); " -q, --query : Query the current settings for the device\n"
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to address, default 1 if absent\n" ); "Options for use with monitors:\n"
fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); " -m, --monitor <monitor_id> : Specify which monitor to address, default 1 if absent\n"
fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); " -q, --query : Query the current settings for the monitor\n"
fprintf( stderr, " 3 = alert, 4 = tape\n" ); " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n"
fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); " 3 = alert, 4 = tape\n"
fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); " -B, --brightness [value] : Output the current brightness, set to value if given \n"
fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); " -C, --contrast [value] : Output the current contrast, set to value if given \n"
fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); " -H, --hue [value] : Output the current hue, set to value if given \n"
fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as <monitor_name>.jpg, last image captured\n" ); " -O, --colour [value] : Output the current colour, set to value if given \n"
fprintf( stderr, " or specified ring buffer index if given.\n" ); " -i, --image [image_index] : Write captured image to disk as <monitor_name>.jpg, last image captured\n"
fprintf( stderr, " -S, --scale <scale_%%ge> : With --image specify any scaling (in %%) to be applied to the image\n" ); " or specified ring buffer index if given.\n"
fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); " -S, --scale <scale_%%ge> : With --image specify any scaling (in %%) to be applied to the image\n"
fprintf( stderr, " ring buffer index if given\n" ); " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n"
fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); " ring buffer index if given\n"
fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); " -R, --read_index : Output ring buffer read index\n"
fprintf( stderr, " -e, --event : Output last event index\n" ); " -W, --write_index : Output ring buffer write index\n"
fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); " -e, --event : Output last event index\n"
fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to <monitor_name>-Zones.jpg\n" ); " -f, --fps : Output last Frames Per Second captured reading\n"
fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); " -z, --zones : Write last captured image overlaid with zones to <monitor_name>-Zones.jpg\n"
fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n"
fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n"
fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n"
fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); " -L, --reload : Signal monitor to reload settings\n"
fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); " -E, --enable : Enable detection, wake monitor up\n"
fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); " -D, --disable : Disable detection, put monitor to sleep\n"
fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n"
fprintf( stderr, " -U, --username <username> : When running in authenticated mode the username and\n" ); " -r, --resume : Resume detection after a suspend\n"
fprintf( stderr, " -P, --password <password> : password combination of the given user\n" ); " -U, --username <username> : When running in authenticated mode the username and\n"
fprintf( stderr, " -A, --auth <authentication> : Pass authentication hash string instead of user details\n" ); " -P, --password <password> : password combination of the given user\n"
" -A, --auth <authentication> : Pass authentication hash string instead of user details\n"
"", stderr );
exit( status ); exit(status);
} }
typedef enum { typedef enum {
@ -166,7 +169,7 @@ typedef enum {
ZMU_LIST = 0x10000000, ZMU_LIST = 0x10000000,
} Function; } Function;
bool ValidateAccess( User *user, int mon_id, int function ) { bool ValidateAccess(User *user, int mon_id, int function) {
bool allowed = true; bool allowed = true;
if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) { if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) {
if ( user->getStream() < User::PERM_VIEW ) if ( user->getStream() < User::PERM_VIEW )
@ -185,26 +188,30 @@ bool ValidateAccess( User *user, int mon_id, int function ) {
allowed = false; allowed = false;
} }
if ( mon_id > 0 ) { if ( mon_id > 0 ) {
if ( !user->canAccess( mon_id ) ) { if ( !user->canAccess(mon_id) ) {
allowed = false; allowed = false;
} }
} }
if ( !allowed ) { return allowed;
fprintf( stderr, "Error, insufficient privileges for requested action\n" );
exit( -1 );
}
return( allowed );
} }
int main( int argc, char *argv[] ) { int exit_zmu(int exit_code) {
logTerm();
zmDbClose();
exit(exit_code);
return exit_code;
}
int main(int argc, char *argv[]) {
if ( access(ZM_CONFIG, R_OK) != 0 ) { if ( access(ZM_CONFIG, R_OK) != 0 ) {
fprintf( stderr, "Can't open %s: %s\n", ZM_CONFIG, strerror(errno) ); fprintf(stderr, "Can't open %s: %s\n", ZM_CONFIG, strerror(errno));
exit( -1 ); exit(-1);
} }
self = argv[0]; self = argv[0];
srand( getpid() * time( 0 ) ); srand(getpid() * time(0));
static struct option long_options[] = { static struct option long_options[] = {
{"device", 2, 0, 'd'}, {"device", 2, 0, 'd'},
@ -266,8 +273,8 @@ int main( int argc, char *argv[] ) {
while (1) { while (1) {
int option_index = 0; int option_index = 0;
int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); int c = getopt_long(argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index);
if (c == -1) { if ( c == -1 ) {
break; break;
} }
@ -288,7 +295,7 @@ int main( int argc, char *argv[] ) {
case 'i': case 'i':
function |= ZMU_IMAGE; function |= ZMU_IMAGE;
if ( optarg ) if ( optarg )
image_idx = atoi( optarg ); image_idx = atoi(optarg);
break; break;
case 'S': case 'S':
scale = atoi(optarg); scale = atoi(optarg);
@ -296,7 +303,7 @@ int main( int argc, char *argv[] ) {
case 't': case 't':
function |= ZMU_TIME; function |= ZMU_TIME;
if ( optarg ) if ( optarg )
image_idx = atoi( optarg ); image_idx = atoi(optarg);
break; break;
case 'R': case 'R':
function |= ZMU_READ_IDX; function |= ZMU_READ_IDX;
@ -345,22 +352,22 @@ int main( int argc, char *argv[] ) {
case 'B': case 'B':
function |= ZMU_BRIGHTNESS; function |= ZMU_BRIGHTNESS;
if ( optarg ) if ( optarg )
brightness = atoi( optarg ); brightness = atoi(optarg);
break; break;
case 'C': case 'C':
function |= ZMU_CONTRAST; function |= ZMU_CONTRAST;
if ( optarg ) if ( optarg )
contrast = atoi( optarg ); contrast = atoi(optarg);
break; break;
case 'H': case 'H':
function |= ZMU_HUE; function |= ZMU_HUE;
if ( optarg ) if ( optarg )
hue = atoi( optarg ); hue = atoi(optarg);
break; break;
case 'O': case 'O':
function |= ZMU_COLOUR; function |= ZMU_COLOUR;
if ( optarg ) if ( optarg )
colour = atoi( optarg ); colour = atoi(optarg);
break; break;
case 'U': case 'U':
username = optarg; username = optarg;
@ -377,41 +384,39 @@ int main( int argc, char *argv[] ) {
break; break;
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
case 'h': case 'h':
Usage( 0 ); case '?':
Usage(0);
break; break;
case 'l': case 'l':
function |= ZMU_LIST; function |= ZMU_LIST;
break; break;
case '?':
Usage();
break;
default: default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break; break;
} }
} }
if (optind < argc) { if ( optind < argc ) {
fprintf( stderr, "Extraneous options, " ); fprintf(stderr, "Extraneous options, ");
while (optind < argc) while (optind < argc)
fprintf( stderr, "%s ", argv[optind++]); fprintf(stderr, "%s ", argv[optind++]);
fprintf( stderr, "\n"); fprintf(stderr, "\n");
Usage(); Usage();
} }
if ( device && !(function&ZMU_QUERY) ) { if ( device && !(function&ZMU_QUERY) ) {
fprintf( stderr, "Error, -d option cannot be used with this option\n" ); fprintf(stderr, "Error, -d option cannot be used with this option\n");
Usage(); Usage();
} }
if ( scale != -1 && !(function&ZMU_IMAGE) ) { if ( scale != -1 && !(function&ZMU_IMAGE) ) {
fprintf( stderr, "Error, -S option cannot be used with this option\n" ); fprintf(stderr, "Error, -S option cannot be used with this option\n");
Usage(); Usage();
} }
//printf( "Monitor %d, Function %d\n", mon_id, function ); //printf( "Monitor %d, Function %d\n", mon_id, function );
zmLoadConfig(); zmLoadConfig();
logInit( "zmu" ); logInit("zmu");
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
@ -419,62 +424,66 @@ int main( int argc, char *argv[] ) {
User *user = 0; User *user = 0;
if ( config.opt_use_auth ) { if ( config.opt_use_auth ) {
if ( strcmp( config.auth_relay, "none" ) == 0 ) { if ( strcmp(config.auth_relay, "none") == 0 ) {
if ( !username ) { if ( !username ) {
fprintf( stderr, "Error, username must be supplied\n" ); fprintf(stderr, "Error, username must be supplied\n");
exit( -1 ); exit_zmu(-1);
} }
if ( username ) { if ( username ) {
user = zmLoadUser( username ); user = zmLoadUser(username);
} }
} else { } else {
if ( !(username && password) && !auth ) { if ( !(username && password) && !auth ) {
fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); fprintf(stderr, "Error, username and password or auth string must be supplied\n");
exit( -1 ); exit_zmu(-1);
} }
//if ( strcmp( config.auth_relay, "hashed" ) == 0 ) //if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{ {
if ( auth ) { if ( auth ) {
user = zmLoadAuthUser( auth, false ); user = zmLoadAuthUser(auth, false);
} }
} }
//else if ( strcmp( config.auth_relay, "plain" ) == 0 ) //else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{ {
if ( username && password ) { if ( username && password ) {
user = zmLoadUser( username, password ); user = zmLoadUser(username, password);
} }
} }
} }
if ( !user ) { if ( !user ) {
fprintf( stderr, "Error, unable to authenticate user\n" ); fprintf(stderr, "Error, unable to authenticate user\n");
exit( -1 ); return exit_zmu(-1);
} }
ValidateAccess( user, mon_id, function ); if ( !ValidateAccess(user, mon_id, function) ) {
} fprintf(stderr, "Error, insufficient privileges for requested action\n");
exit_zmu(-1);
}
} // end if auth
if ( mon_id > 0 ) { if ( mon_id > 0 ) {
Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); fprintf(stderr,"Monitor %d\n", mon_id);
Monitor *monitor = Monitor::Load(mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY);
if ( monitor ) { if ( monitor ) {
fprintf(stderr,"Monitor %d(%s)\n", monitor->Id(), monitor->Name());
if ( verbose ) { if ( verbose ) {
printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name());
} }
if ( ! monitor->connect() ) { if ( ! monitor->connect() ) {
Error( "Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name() ); Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name());
exit( -1 ); exit_zmu(-1);
} }
char separator = ' '; char separator = ' ';
bool have_output = false; bool have_output = false;
if ( function & ZMU_STATE ) { if ( function & ZMU_STATE ) {
Monitor::State state = monitor->GetState(); Monitor::State state = monitor->GetState();
if ( verbose ) if ( verbose )
printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); printf("Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"));
else { else {
if ( have_output ) printf( "%c", separator ); if ( have_output ) printf("%c", separator);
printf( "%d", state ); printf("%d", state);
have_output = true; have_output = true;
} }
} }
@ -514,10 +523,10 @@ int main( int argc, char *argv[] ) {
} }
if ( function & ZMU_EVENT ) { if ( function & ZMU_EVENT ) {
if ( verbose ) if ( verbose )
printf( "Last event id: %d\n", monitor->GetLastEventId() ); printf( "Last event id: %" PRIu64 "\n", monitor->GetLastEventId() );
else { else {
if ( have_output ) printf( "%c", separator ); if ( have_output ) printf( "%c", separator );
printf( "%d", monitor->GetLastEventId() ); printf( "%" PRIu64, monitor->GetLastEventId() );
have_output = true; have_output = true;
} }
} }
@ -660,8 +669,8 @@ int main( int argc, char *argv[] ) {
} }
delete monitor; delete monitor;
} else { } else {
fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); fprintf(stderr, "Error, invalid monitor id %d\n", mon_id);
exit( -1 ); exit_zmu(-1);
} }
} else { } else {
if ( function & ZMU_QUERY ) { if ( function & ZMU_QUERY ) {
@ -669,10 +678,10 @@ int main( int argc, char *argv[] ) {
char vidString[0x10000] = ""; char vidString[0x10000] = "";
bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose );
printf( "%s", vidString ); printf( "%s", vidString );
exit( ok?0:-1 ); exit_zmu( ok?0:-1 );
#else // ZM_HAS_V4L #else // ZM_HAS_V4L
fprintf( stderr, "Error, video4linux is required for device querying\n" ); fprintf( stderr, "Error, video4linux is required for device querying\n" );
exit( -1 ); exit_zmu( -1 );
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
} }
@ -685,13 +694,13 @@ int main( int argc, char *argv[] ) {
if ( mysql_query( &dbconn, sql.c_str() ) ) { if ( mysql_query( &dbconn, sql.c_str() ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit_zmu( mysql_errno( &dbconn ) );
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit_zmu( mysql_errno( &dbconn ) );
} }
Debug( 1, "Got %d monitors", mysql_num_rows( result ) ); Debug( 1, "Got %d monitors", mysql_num_rows( result ) );
@ -704,7 +713,7 @@ int main( int argc, char *argv[] ) {
Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY );
if ( monitor && monitor->connect() ) { if ( monitor && monitor->connect() ) {
struct timeval tv = monitor->GetTimestamp(); struct timeval tv = monitor->GetTimestamp();
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n",
monitor->Id(), monitor->Id(),
function, function,
monitor->GetState(), monitor->GetState(),
@ -738,8 +747,5 @@ int main( int argc, char *argv[] ) {
} }
delete user; delete user;
logTerm(); return exit_zmu(0);
zmDbClose();
return( 0 );
} }

View File

@ -27,7 +27,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then
echo echo
mkdir -p ./zmrepo mkdir -p ./zmrepo
ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect)" ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect 2>&1)"
if [ -z "$ssh_mntchk" ]; then if [ -z "$ssh_mntchk" ]; then
echo echo

View File

@ -19,20 +19,7 @@ checksanity () {
exit 1 exit 1
fi fi
done done
if [ "${OS}" == "el" ] && [ "${DIST}" == "6" ]; then
type repoquery 2>&1 > /dev/null
if [ $? -ne 0 ]; then
echo
echo "ERROR: The script cannot find the required command \"reqoquery\"."
echo "This command is required in order to build ZoneMinder on el6."
echo "Please install the \"yum-utils\" package then try again."
echo
exit 1
fi
fi
# Verify OS & DIST environment variables have been set before calling this script # Verify OS & DIST environment variables have been set before calling this script
if [ -z "${OS}" ] || [ -z "${DIST}" ]; then if [ -z "${OS}" ] || [ -z "${DIST}" ]; then
echo "ERROR: both OS and DIST environment variables must be set" echo "ERROR: both OS and DIST environment variables must be set"
@ -103,7 +90,7 @@ commonprep () {
fi fi
# Rpm builds are broken in latest packpack master. Temporarily roll back. # Rpm builds are broken in latest packpack master. Temporarily roll back.
git -C packpack checkout 7cf23ee #git -C packpack checkout 7cf23ee
# Patch packpack # Patch packpack
patch --dry-run --silent -f -p1 < utils/packpack/packpack-rpm.patch patch --dry-run --silent -f -p1 < utils/packpack/packpack-rpm.patch
@ -118,27 +105,40 @@ commonprep () {
fi fi
# fix 32bit rpm builds # fix 32bit rpm builds
patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch # FIXME: breaks arm rpm builds
if [ $? -eq 0 ]; then #patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
patch -p1 < utils/packpack/setarch.patch #if [ $? -eq 0 ]; then
fi # patch -p1 < utils/packpack/setarch.patch
#fi
# The rpm specfile requires we download the tarball and manually move it into place # The rpm specfile requires we download each submodule as a tarball then manually move it into place
# Might as well do this for Debian as well, rather than git submodule init # Might as well do this for Debian as well, rather than git submodule init
CRUDVER="3.0.10" CRUDVER="3.1.0-zm"
if [ -e "build/crud-${CRUDVER}.tar.gz" ]; then if [ -e "build/crud-${CRUDVER}.tar.gz" ]; then
echo "Found existing Crud ${CRUDVER} tarball..." echo "Found existing Crud ${CRUDVER} tarball..."
else else
echo "Retrieving Crud ${CRUDVER} submodule..." echo "Retrieving Crud ${CRUDVER} submodule..."
curl -L https://github.com/FriendsOfCake/crud/archive/v${CRUDVER}.tar.gz > build/crud-${CRUDVER}.tar.gz curl -L https://github.com/ZoneMinder/crud/archive/v${CRUDVER}.tar.gz > build/crud-${CRUDVER}.tar.gz
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Crud tarball retreival failed..." echo "ERROR: Crud tarball retreival failed..."
exit 1 exit 1
fi fi
fi fi
CEBVER="1.0-zm"
if [ -e "build/cakephp-enum-behavior-${CEBVER}.tar.gz" ]; then
echo "Found existing CakePHP-Enum-Behavior ${CEBVER} tarball..."
else
echo "Retrieving CakePHP-Enum-Behavior ${CEBVER} submodule..."
curl -L https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/${CEBVER}.tar.gz > build/cakephp-enum-behavior-${CEBVER}.tar.gz
if [ $? -ne 0 ]; then
echo "ERROR: CakePHP-Enum-Behavior tarball retreival failed..."
exit 1
fi
fi
} }
# Uncompress the Crud tarball and move it into place # Uncompress the submodule tarballs and move them into place
movecrud () { movecrud () {
if [ -e "web/api/app/Plugin/Crud/LICENSE.txt" ]; then if [ -e "web/api/app/Plugin/Crud/LICENSE.txt" ]; then
echo "Crud plugin already installed..." echo "Crud plugin already installed..."
@ -148,6 +148,14 @@ movecrud () {
rmdir web/api/app/Plugin/Crud rmdir web/api/app/Plugin/Crud
mv -f crud-${CRUDVER} web/api/app/Plugin/Crud mv -f crud-${CRUDVER} web/api/app/Plugin/Crud
fi fi
if [ -e "web/api/app/Plugin/CakePHP-Enum-Behavior/readme.md" ]; then
echo "CakePHP-Enum-Behavior plugin already installed..."
else
echo "Unpacking CakePHP-Enum-Behavior plugin..."
tar -xzf build/cakephp-enum-behavior-${CEBVER}.tar.gz
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
mv -f crud-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
fi
} }
# previsouly part of installzm.sh # previsouly part of installzm.sh
@ -259,10 +267,43 @@ execpackpack () {
fi fi
} }
# Check for connectivity with the deploy target host
checkdeploytarget () {
echo
echo "Checking Internet connectivity with the deploy host ${DEPLOYTARGET}"
echo
ping -c 1 ${DEPLOYTARGET}
if [ $? -ne 0 ]; then
echo
echo "*** WARNING: THERE WAS A PROBLEM CONNECTING TO THE DEPLOY HOST ***"
echo
echo "Printing additional diagnostic information..."
echo
echo "*** NSLOOKUP ***"
echo
nslookup ${DEPLOYTARGET}
echo
echo "*** TRACEROUTE ***"
echo
traceroute -w 2 -m 15 ${DEPLOYTARGET}
fi
}
################ ################
# MAIN PROGRAM # # MAIN PROGRAM #
################ ################
# Set the hostname we will deploy packages to
DEPLOYTARGET="zmrepo.zoneminder.com"
# If we are running inside Travis then verify we can connect to the target host machine
if [ "${TRAVIS}" == "true" ]; then
checkdeploytarget
fi
checksanity checksanity
# We don't want to build packages for all supported distros after every commit # We don't want to build packages for all supported distros after every commit
@ -283,20 +324,12 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
rm -rf web/api/app/Plugin/Crud rm -rf web/api/app/Plugin/Crud
mkdir web/api/app/Plugin/Crud mkdir web/api/app/Plugin/Crud
# We use zmrepo to build el6 only. All other redhat distros use rpm fusion reporpm="rpmfusion-free-release"
if [ "${OS}" == "el" ] && [ "${DIST}" == "6" ]; then dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm"
baseurl="https://zmrepo.zoneminder.com/el/${DIST}/x86_64/"
reporpm="zmrepo"
# Let repoquery determine the full url and filename to the latest zmrepo package
dlurl=`repoquery --archlist=noarch --repofrompath=zmpackpack,${baseurl} --repoid=zmpackpack --qf="%{location}" ${reporpm} 2> /dev/null`
else
reporpm="rpmfusion-free-release"
dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm"
fi
# Give our downloaded repo rpm a common name so redhat_package.mk can find it # Give our downloaded repo rpm a common name so redhat_package.mk can find it
if [ -n "$dlurl" ] && [ $? -eq 0 ]; then if [ -n "$dlurl" ] && [ $? -eq 0 ]; then
echo "Retrieving ${reporpm} repo rpm..."gd echo "Retrieving ${reporpm} repo rpm..."
curl $dlurl > build/external-repo.noarch.rpm curl $dlurl > build/external-repo.noarch.rpm
else else
echo "ERROR: Failed to retrieve ${reporpm} repo rpm..." echo "ERROR: Failed to retrieve ${reporpm} repo rpm..."

View File

@ -1 +1 @@
1.31.41 1.31.44

View File

@ -1,5 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
</IfModule>

View File

@ -109,83 +109,83 @@ $statusData = array(
'MaxScore' => true, 'MaxScore' => true,
), ),
), ),
'event' => array( 'event' => array(
'permission' => 'Events', 'permission' => 'Events',
'table' => 'Events', 'table' => 'Events',
'limit' => 1, 'limit' => 1,
'selector' => 'Events.Id', 'selector' => 'Events.Id',
'elements' => array( 'elements' => array(
'Id' => array( 'sql' => 'Events.Id' ), 'Id' => array( 'sql' => 'Events.Id' ),
'MonitorId' => true, 'MonitorId' => true,
'MonitorName' => array('sql' => '(SELECT Monitors.Name FROM Monitors WHERE Monitors.Id = Events.MonitorId)'), 'MonitorName' => array('sql' => '(SELECT Monitors.Name FROM Monitors WHERE Monitors.Id = Events.MonitorId)'),
'Name' => true, 'Name' => true,
'Cause' => true, 'Cause' => true,
'StartTime' => true, 'StartTime' => true,
'StartTimeShort' => array( 'sql' => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ), 'StartTimeShort' => array( 'sql' => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ),
'EndTime' => true, 'EndTime' => true,
'Width' => true, 'Width' => true,
'Height' => true, 'Height' => true,
'Length' => true, 'Length' => true,
'Frames' => true, 'Frames' => true,
'DefaultVideo' => true, 'DefaultVideo' => true,
'AlarmFrames' => true, 'AlarmFrames' => true,
'TotScore' => true, 'TotScore' => true,
'AvgScore' => true, 'AvgScore' => true,
'MaxScore' => true, 'MaxScore' => true,
'Archived' => true, 'Archived' => true,
'Videoed' => true, 'Videoed' => true,
'Uploaded' => true, 'Uploaded' => true,
'Emailed' => true, 'Emailed' => true,
'Messaged' => true, 'Messaged' => true,
'Executed' => true, 'Executed' => true,
'Notes' => true, 'Notes' => true,
'MinFrameId' => array( 'sql' => '(SELECT min(Frames.FrameId) FROM Frames WHERE EventId=Events.Id)' ), 'MinFrameId' => array( 'sql' => '(SELECT min(Frames.FrameId) FROM Frames WHERE EventId=Events.Id)' ),
'MaxFrameId' => array( 'sql' => '(SELECT max(Frames.FrameId) FROM Frames WHERE Events.Id = Frames.EventId)' ), 'MaxFrameId' => array( 'sql' => '(SELECT max(Frames.FrameId) FROM Frames WHERE Events.Id = Frames.EventId)' ),
'MinFrameDelta' => array( 'sql' => '(SELECT min(Frames.Delta) FROM Frames WHERE Events.Id = Frames.EventId)' ), 'MinFrameDelta' => array( 'sql' => '(SELECT min(Frames.Delta) FROM Frames WHERE Events.Id = Frames.EventId)' ),
'MaxFrameDelta' => array( 'sql' => '(SELECT max(Frames.Delta) FROM Frames WHERE Events.Id = Frames.EventId)' ), 'MaxFrameDelta' => array( 'sql' => '(SELECT max(Frames.Delta) FROM Frames WHERE Events.Id = Frames.EventId)' ),
), ),
), ),
'frames' => array( 'frames' => array(
'permission' => 'Events', 'permission' => 'Events',
'table' => 'Frames', 'table' => 'Frames',
'selector' => 'EventId', 'selector' => 'EventId',
'elements' => array( 'elements' => array(
'EventId' => true, 'EventId' => true,
'FrameId' => true, 'FrameId' => true,
'Type' => true, 'Type' => true,
'Delta' => true, 'Delta' => true,
), ),
), ),
'frame' => array( 'frame' => array(
'permission' => 'Events', 'permission' => 'Events',
'table' => 'Frames', 'table' => 'Frames',
'limit' => 1, 'limit' => 1,
'selector' => array( array( 'table' => 'Events', 'join' => 'Events.Id = Frames.EventId', 'selector'=>'Events.Id' ), 'Frames.FrameId' ), 'selector' => array( array( 'table' => 'Events', 'join' => 'Events.Id = Frames.EventId', 'selector'=>'Events.Id' ), 'Frames.FrameId' ),
'elements' => array( 'elements' => array(
//'Id' => array( 'sql' => 'Frames.FrameId' ), //'Id' => array( 'sql' => 'Frames.FrameId' ),
'FrameId' => true, 'FrameId' => true,
'EventId' => true, 'EventId' => true,
'Type' => true, 'Type' => true,
'TimeStamp' => true, 'TimeStamp' => true,
'TimeStampShort' => array( 'sql' => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ), 'TimeStampShort' => array( 'sql' => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ),
'Delta' => true, 'Delta' => true,
'Score' => true, 'Score' => true,
//'Image' => array( 'postFunc' => 'getFrameImage' ), //'Image' => array( 'postFunc' => 'getFrameImage' ),
), ),
), ),
'frameimage' => array( 'frameimage' => array(
'permission' => 'Events', 'permission' => 'Events',
'func' => 'getFrameImage()' 'func' => 'getFrameImage()'
), ),
'nearframe' => array( 'nearframe' => array(
'permission' => 'Events', 'permission' => 'Events',
'func' => 'getNearFrame()' 'func' => 'getNearFrame()'
), ),
'nearevents' => array( 'nearevents' => array(
'permission' => 'Events', 'permission' => 'Events',
'func' => 'getNearEvents()' 'func' => 'getNearEvents()'
) )
); );
function collectData() { function collectData() {
global $statusData; global $statusData;
@ -302,8 +302,8 @@ function collectData() {
} }
} }
} }
#print_r( $data ); #Logger::Debug(print_r($data, true));
return( $data ); return $data;
} }
$data = collectData(); $data = collectData();
@ -345,8 +345,8 @@ function getFrameImage() {
$eventId = $_REQUEST['id'][0]; $eventId = $_REQUEST['id'][0];
$frameId = $_REQUEST['id'][1]; $frameId = $_REQUEST['id'][1];
$sql = 'select * from Frames where EventId = ? and FrameId = ?'; $sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?';
if ( !($frame = dbFetchOne( $sql, NULL, array( $eventId, $frameId ) )) ) { if ( !($frame = dbFetchOne( $sql, NULL, array($eventId, $frameId ) )) ) {
$frame = array(); $frame = array();
$frame['EventId'] = $eventId; $frame['EventId'] = $eventId;
$frame['FrameId'] = $frameId; $frame['FrameId'] = $frameId;

View File

@ -145,7 +145,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
} }
case MSG_DATA_EVENT : case MSG_DATA_EVENT :
{ {
$data = unpack( "ltype/ievent/iprogress/irate/izoom/Cpaused", $msg ); $data = unpack( "ltype/Pevent/iprogress/irate/izoom/Cpaused", $msg );
//$data['progress'] = sprintf( "%.2f", $data['progress'] ); //$data['progress'] = sprintf( "%.2f", $data['progress'] );
$data['rate'] /= RATE_BASE; $data['rate'] /= RATE_BASE;
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 ); $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );

View File

@ -1,6 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</IfModule>

View File

@ -1,6 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</IfModule>

@ -1 +1 @@
Subproject commit 1351dde6b4c75b215099ae8bcf5a21d6c6e10298 Subproject commit c3976f1478c681b0bbc132ec3a3e82c3984eeed5

View File

@ -1,7 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</IfModule>

View File

@ -1,5 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>

View File

@ -1,30 +1,30 @@
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ::
:: Bake is a shell script for running CakePHP bake script :: Bake is a shell script for running CakePHP bake script
:: ::
:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) :: CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) :: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: ::
:: Licensed under The MIT License :: Licensed under The MIT License
:: Redistributions of files must retain the above copyright notice. :: Redistributions of files must retain the above copyright notice.
:: ::
:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) :: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: @link https://cakephp.org CakePHP(tm) Project :: @link https://cakephp.org CakePHP(tm) Project
:: @package app.Console :: @package app.Console
:: @since CakePHP(tm) v 2.0 :: @since CakePHP(tm) v 2.0
:: ::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: In order for this script to work as intended, the cake\console\ folder must be in your PATH :: In order for this script to work as intended, the cake\console\ folder must be in your PATH
@echo. @echo.
@echo off @echo off
SET app=%0 SET app=%0
SET lib=%~dp0 SET lib=%~dp0
php -q "%lib%cake.php" -working "%CD% " %* php -q "%lib%cake.php" -working "%CD% " %*
echo. echo.
exit /B %ERRORLEVEL% exit /B %ERRORLEVEL%

View File

@ -1,6 +0,0 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

View File

@ -1,28 +1,28 @@
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: ::
:: Bake is a shell script for running CakePHP bake script :: Bake is a shell script for running CakePHP bake script
:: ::
:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) :: CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) :: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: ::
:: Licensed under The MIT License :: Licensed under The MIT License
:: Redistributions of files must retain the above copyright notice. :: Redistributions of files must retain the above copyright notice.
:: ::
:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) :: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: @link https://cakephp.org CakePHP(tm) Project :: @link https://cakephp.org CakePHP(tm) Project
:: @package Cake.Console :: @package Cake.Console
:: @since CakePHP(tm) v 1.2.0.5012 :: @since CakePHP(tm) v 1.2.0.5012
:: @license https://opensource.org/licenses/mit-license.php MIT License :: @license https://opensource.org/licenses/mit-license.php MIT License
:: ::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off @echo off
SET app=%0 SET app=%0
SET lib=%~dp0 SET lib=%~dp0
php -q "%lib%cake.php" -working "%CD% " %* php -q "%lib%cake.php" -working "%CD% " %*
echo. echo.
exit /B %ERRORLEVEL% exit /B %ERRORLEVEL%

View File

@ -1,5 +1,7 @@
<?php <?php
$event_cache = array();
class Event { class Event {
private $fields = array( private $fields = array(
@ -15,12 +17,12 @@ class Event {
public function __construct( $IdOrRow = null ) { public function __construct( $IdOrRow = null ) {
$row = NULL; $row = NULL;
if ( $IdOrRow ) { if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) ); $row = dbFetchOne('SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) { if ( ! $row ) {
Error('Unable to load Event record for Id=' . $IdOrRow ); Error('Unable to load Event record for Id=' . $IdOrRow );
} }
} elseif ( is_array( $IdOrRow ) ) { } elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow; $row = $IdOrRow;
} else { } else {
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
@ -31,16 +33,18 @@ class Event {
return; return;
} }
if ( $row ) { if ( $row ) {
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
$this->{$k} = $v; $this->{$k} = $v;
} }
} else { global $event_cache;
$event_cache[$row['Id']] = $this;
} else {
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Error('No row for Event ' . $IdOrRow . " from $file:$line"); Error('No row for Event ' . $IdOrRow . " from $file:$line");
} }
} # end if isset($IdOrRow) } # end if isset($IdOrRow)
} // end function __construct } // end function __construct
@ -48,10 +52,11 @@ class Event {
if ( $new ) { if ( $new ) {
$this->{'Storage'} = $new; $this->{'Storage'} = $new;
} }
if ( ! ( array_key_exists( 'Storage', $this ) and $this->{'Storage'} ) ) { if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
$this->{'Storage'} = isset($this->{'StorageId'}) ? if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
Storage::find_one(array('Id'=>$this->{'StorageId'})) : $this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'}));
new Storage(NULL); if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) )
$this->{'Storage'} = new Storage(NULL);
} }
return $this->{'Storage'}; return $this->{'Storage'};
} }
@ -70,12 +75,12 @@ class Event {
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Warning( "Unknown function call Event->$fn from $file:$line" ); Warning("Unknown function call Event->$fn from $file:$line");
} }
} }
public function Time() { public function Time() {
if ( ! isset( $this->{'Time'} ) ) { if ( ! isset($this->{'Time'}) ) {
$this->{'Time'} = strtotime($this->{'StartTime'}); $this->{'Time'} = strtotime($this->{'StartTime'});
} }
return $this->{'Time'}; return $this->{'Time'};
@ -97,7 +102,7 @@ class Event {
$event_path = $this->{'MonitorId'} .'/'.$this->{'Id'}; $event_path = $this->{'MonitorId'} .'/'.$this->{'Id'};
} }
return( $event_path ); return $event_path;
} // end function Relative_Path() } // end function Relative_Path()
public function Link_Path() { public function Link_Path() {
@ -161,6 +166,8 @@ class Event {
} # end Event->delete } # end Event->delete
public function getStreamSrc( $args=array(), $querySep='&' ) { public function getStreamSrc( $args=array(), $querySep='&' ) {
if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) { if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) {
$streamSrc = ZM_BASE_PROTOCOL.'://'; $streamSrc = ZM_BASE_PROTOCOL.'://';
$Monitor = $this->Monitor(); $Monitor = $this->Monitor();
@ -174,7 +181,19 @@ class Event {
$args['eid'] = $this->{'Id'}; $args['eid'] = $this->{'Id'};
$args['view'] = 'view_video'; $args['view'] = 'view_video';
} else { } else {
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; $streamSrc = ZM_BASE_PROTOCOL.'://';
if ( $this->Storage()->ServerId() ) {
$Server = $this->Storage()->Server();
$streamSrc .= $Server->Hostname();
if ( ZM_MIN_STREAMING_PORT ) {
$streamSrc .= ':'.(ZM_MIN_STREAMING_PORT+$this->{'MonitorId'});
}
} else if ( ZM_MIN_STREAMING_PORT ) {
$streamSrc .= $_SERVER['SERVER_NAME'].':'.(ZM_MIN_STREAMING_PORT+$this->{'MonitorId'});
} else {
$streamSrc .= $_SERVER['HTTP_HOST'];
}
$streamSrc .= ZM_PATH_ZMS;
$args['source'] = 'event'; $args['source'] = 'event';
$args['event'] = $this->{'Id'}; $args['event'] = $this->{'Id'};
@ -207,8 +226,8 @@ class Event {
$this->{'DiskSpace'} = $new; $this->{'DiskSpace'} = $new;
} }
if ( null === $this->{'DiskSpace'} ) { if ( null === $this->{'DiskSpace'} ) {
$this->{'DiskSpace'} = folder_size( $this->Path() ); $this->{'DiskSpace'} = folder_size($this->Path());
dbQuery( 'UPDATE Events SET DiskSpace=? WHERE Id=?', array( $this->{'DiskSpace'}, $this->{'Id'} ) ); dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
} }
return $this->{'DiskSpace'}; return $this->{'DiskSpace'};
} }
@ -253,7 +272,7 @@ class Event {
// frame is an array representing the db row for a frame. // frame is an array representing the db row for a frame.
function getImageSrc($frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false) { function getImageSrc($frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false) {
$Storage = new Storage(isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL); $Storage = $this->Storage();
$Event = $this; $Event = $this;
$eventPath = $Event->Path(); $eventPath = $Event->Path();
@ -370,6 +389,25 @@ class Event {
return $imageData; return $imageData;
} }
public static function find_one( $parameters = null, $options = null ) {
global $event_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($event_cache[$parameters['Id']]) ) {
return $event_cache[$parameters['Id']];
}
$results = Event::find_all( $parameters, $options );
if ( count($results) > 1 ) {
Error("Event Returned more than 1");
return $results[0];
} else if ( count($results) ) {
return $results[0];
} else {
return null;
}
}
public static function find_all( $parameters = null, $options = null ) { public static function find_all( $parameters = null, $options = null ) {
$filters = array(); $filters = array();
$sql = 'SELECT * FROM Events '; $sql = 'SELECT * FROM Events ';

View File

@ -73,18 +73,19 @@ class Frame {
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Error("Invalid value for limit($limit) passed to Frame::find from $file:$line"); Error("Invalid value for limit($limit) passed to Frame::find from $file:$line");
return; return array();
} }
} }
$results = dbFetchAll( $sql, NULL, $values ); $results = dbFetchAll($sql, NULL, $values);
if ( $results ) { if ( $results ) {
return array_map( function($id){ return new Frame($id); }, $results ); return array_map( function($id){ return new Frame($id); }, $results );
} }
return array();
} }
public static function find_one( $parameters = array() ) { public static function find_one( $parameters = array() ) {
$results = Frame::find( $parameters, 1 ); $results = Frame::find( $parameters, 1 );
if ( ! sizeof( $results ) ) { if ( ! sizeof($results) ) {
return; return;
} }
return $results[0]; return $results[0];

View File

@ -20,7 +20,7 @@ class Group {
if ( ! $row ) { if ( ! $row ) {
Error('Unable to load Group record for Id=' . $IdOrRow); Error('Unable to load Group record for Id=' . $IdOrRow);
} }
} elseif ( is_array( $IdOrRow ) ) { } elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow; $row = $IdOrRow;
} else { } else {
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
@ -36,8 +36,8 @@ class Group {
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
$this->{$k} = $v; $this->{$k} = $v;
} }
$group_cache[$row['Id']] = $this;
} }
$group_cache[$row['Id']] = $this;
} // end function __construct } // end function __construct
public function __call($fn, array $args) { public function __call($fn, array $args) {
@ -58,7 +58,7 @@ class Group {
} }
} }
public static function find_one( $parameters = null, $options = null ) { public static function find_one($parameters = null, $options = null) {
global $group_cache; global $group_cache;
if ( if (
( count($parameters) == 1 ) and ( count($parameters) == 1 ) and
@ -109,13 +109,13 @@ class Group {
} }
public function delete() { public function delete() {
if ( array_key_exists( 'Id', $this ) ) { if ( array_key_exists('Id', $this) ) {
dbQuery( 'DELETE FROM Groups WHERE Id=?', array($this->{'Id'}) );
dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}) ); dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}) );
dbQuery( 'DELETE FROM Groups WHERE Id=?', array($this->{'Id'}) );
if ( isset($_COOKIE['zmGroup']) ) { if ( isset($_COOKIE['zmGroup']) ) {
if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) { if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) {
unset( $_COOKIE['zmGroup'] ); unset($_COOKIE['zmGroup']);
setcookie( 'zmGroup', '', time()-3600*24*2 ); setcookie('zmGroup', '', time()-3600*24*2);
} }
} }
} }
@ -123,16 +123,16 @@ class Group {
public function set( $data ) { public function set( $data ) {
foreach ($data as $k => $v) { foreach ($data as $k => $v) {
if ( is_array( $v ) ) { if ( is_array($v) ) {
$this->{$k} = $v; $this->{$k} = $v;
} else if ( is_string( $v ) ) { } else if ( is_string($v) ) {
$this->{$k} = trim( $v ); $this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) { } else if ( is_integer($v) ) {
$this->{$k} = $v; $this->{$k} = $v;
} else if ( is_bool( $v ) ) { } else if ( is_bool($v) ) {
$this->{$k} = $v; $this->{$k} = $v;
} else { } else {
Error( "Unknown type $k => $v of var " . gettype( $v ) ); Error("Unknown type $k => $v of var " . gettype($v));
$this->{$k} = $v; $this->{$k} = $v;
} }
} }
@ -141,10 +141,10 @@ class Group {
if ( isset($new) ) { if ( isset($new) ) {
$this->{'depth'} = $new; $this->{'depth'} = $new;
} }
if ( ! array_key_exists( 'depth', $this ) or ( $this->{'depth'} == null ) ) { if ( ! array_key_exists('depth', $this) or ($this->{'depth'} == null) ) {
$this->{'depth'} = 1; $this->{'depth'} = 1;
if ( $this->{'ParentId'} != null ) { if ( $this->{'ParentId'} != null ) {
$Parent = new Group( $this->{'ParentId'} ); $Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
$this->{'depth'} += $Parent->depth(); $this->{'depth'} += $Parent->depth();
} }
} }
@ -152,8 +152,8 @@ class Group {
} // end public function depth } // end public function depth
public function MonitorIds( ) { public function MonitorIds( ) {
if ( ! array_key_exists( 'MonitorIds', $this ) ) { if ( ! array_key_exists('MonitorIds', $this) ) {
$this->{'MonitorIds'} = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}) ); $this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
} }
return $this->{'MonitorIds'}; return $this->{'MonitorIds'};
} }
@ -198,12 +198,12 @@ class Group {
} }
} }
function get_options( $Group ) { function get_options($Group) {
global $children; global $children;
$options = array( $Group->Id() => str_repeat('&nbsp;&nbsp;&nbsp;', $Group->depth() ) . $Group->Name() ); $options = array($Group->Id() => str_repeat('&nbsp;&nbsp;&nbsp;', $Group->depth()) . $Group->Name());
if ( isset($children[$Group->Id()]) ) { if ( isset($children[$Group->Id()]) ) {
foreach ( $children[$Group->Id()] as $child ) { foreach ( $children[$Group->Id()] as $child ) {
$options += get_options( $child ); $options += get_options($child);
} }
} }
return $options; return $options;
@ -211,83 +211,34 @@ class Group {
$group_options = array(); $group_options = array();
foreach ( $Groups as $id=>$Group ) { foreach ( $Groups as $id=>$Group ) {
if ( ! $Group->ParentId() ) { if ( ! $Group->ParentId() ) {
$group_options += get_options( $Group ); $group_options += get_options($Group);
} }
} }
return $group_options; return $group_options;
} }
public static function get_group_dropdowns( $selected = null ) { public static function get_group_sql($group_id) {
# This will end up with the group_id of the deepest selection
$group_id = 0;
$depth = 0;
$groups = array();
$parent_group_ids = null;
session_start();
$group_options = array(0=>'All');
while(1) {
$Groups = Group::find_all( array('ParentId'=>$parent_group_ids) );
if ( ! count( $Groups ) )
break;
$parent_group_ids = array();
if ( ! $selected ) {
$selected_group_id = 0;
if ( isset($_REQUEST['group'.$depth]) ) {
$selected_group_id = $group_id = $_SESSION['group'.$depth] = $_REQUEST['group'.$depth];
} else if ( isset( $_SESSION['group'.$depth] ) ) {
$selected_group_id = $group_id = $_SESSION['group'.$depth];
} else if ( isset($_REQUEST['filtering']) ) {
unset($_SESSION['group'.$depth]);
}
} else {
$selected_group_id = $selected;
}
foreach ( $Groups as $Group ) {
if ( ! isset( $groups[$depth] ) ) {
$groups[$depth] = array(0=>'All');
}
$group_options[$Group->Id()] = str_repeat( '&nbsp;', $depth ) . $Group->Name();
$groups[$depth][$Group->Id()] = $Group->Name();
if ( $selected_group_id and ( $selected_group_id == $Group->Id() ) )
$parent_group_ids[] = $Group->Id();
}
//echo htmlSelect( 'group'.$depth, $groups[$depth], $selected_group_id, "this.form.submit();" );
if ( ! count($parent_group_ids) ) break;
$depth += 1;
}
echo htmlSelect( 'groups', $group_options, $selected_group_id, 'this.form.submit();' );
session_write_close();
return $group_id;
} # end public static function get_group_dropdowns()
public static function get_group_sql( $group_id ) {
$groupSql = ''; $groupSql = '';
if ( $group_id ) { if ( $group_id ) {
if ( is_array( $group_id ) ) { if ( is_array($group_id) ) {
$group_id_sql_part = ' IN ('.implode(',', array_map(function(){return '?';}, $group_id ) ).')'; $group_id_sql_part = ' IN ('.implode(',', array_map(function(){return '?';}, $group_id ) ).')';
$MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId'.$group_id_sql_part, 'MonitorId', $group_id ); $MonitorIds = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId'.$group_id_sql_part, 'MonitorId', $group_id);
$MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId'.$group_id_sql_part.')', 'MonitorId', $group_id ) ); $MonitorIds = array_merge($MonitorIds, dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId'.$group_id_sql_part.')', 'MonitorId', $group_id));
} else { } else {
$MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($group_id) ); $MonitorIds = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($group_id));
$MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId = ?)', 'MonitorId', array($group_id) ) ); $MonitorIds = array_merge($MonitorIds, dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId = ?)', 'MonitorId', array($group_id)));
} }
$groupSql = " find_in_set( M.Id, '".implode( ',', $MonitorIds )."' )"; $groupSql = " find_in_set( M.Id, '".implode(',', $MonitorIds)."' )";
} }
return $groupSql; return $groupSql;
} # end public static function get_group_sql( $group_id ) } # end public static function get_group_sql( $group_id )
public static function get_monitors_dropdown( $options = null ) { public static function get_monitors_dropdown($options = null) {
$monitor_id = 0; $monitor_id = 0;
if ( isset( $_REQUEST['monitor_id'] ) ) { if ( isset($_REQUEST['monitor_id']) ) {
$monitor_id = $_REQUEST['monitor_id']; $monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) { } else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId']; $monitor_id = $_COOKIE['zmMonitorId'];
@ -300,14 +251,14 @@ $group_options[$Group->Id()] = str_repeat( '&nbsp;', $depth ) . $Group->Name();
} }
$monitors_dropdown = array(''=>'All'); $monitors_dropdown = array(''=>'All');
foreach ( dbFetchAll( $sql ) as $monitor ) { foreach ( dbFetchAll($sql) as $monitor ) {
if ( !visibleMonitor( $monitor['Id'] ) ) { if ( !visibleMonitor($monitor['Id']) ) {
continue; continue;
} }
$monitors_dropdown[$monitor['Id']] = $monitor['Name']; $monitors_dropdown[$monitor['Id']] = $monitor['Name'];
} }
echo htmlSelect( 'monitor_id', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);') ); echo htmlSelect('monitor_id', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);'));
return $monitor_id; return $monitor_id;
} }
@ -329,6 +280,4 @@ public function Parents() {
} }
} # end class Group } # end class Group
?> ?>

View File

@ -27,6 +27,7 @@ private $defaults = array(
private $status_fields = array( private $status_fields = array(
'AnalysisFPS' => null, 'AnalysisFPS' => null,
'CaptureFPS' => null, 'CaptureFPS' => null,
'CaptureBandwidth' => null,
); );
private $control_fields = array( private $control_fields = array(
'Name' => '', 'Name' => '',
@ -171,6 +172,7 @@ private $control_fields = array(
} }
} # end if isset($IdOrRow) } # end if isset($IdOrRow)
} // end function __construct } // end function __construct
public function Server() { public function Server() {
return new Server( $this->{'ServerId'} ); return new Server( $this->{'ServerId'} );
} }
@ -182,7 +184,7 @@ private $control_fields = array(
return $this->{$fn}; return $this->{$fn};
#array_unshift($args, $this); #array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args); #call_user_func_array( $this->{$fn}, $args);
} else { } else {
if ( array_key_exists($fn, $this->control_fields) ) { if ( array_key_exists($fn, $this->control_fields) ) {
return $this->control_fields{$fn}; return $this->control_fields{$fn};
} else if ( array_key_exists( $fn, $this->defaults ) ) { } else if ( array_key_exists( $fn, $this->defaults ) ) {
@ -255,22 +257,26 @@ private $control_fields = array(
return $this->{'Height'}; return $this->{'Height'};
} }
public function set( $data ) { public function set($data) {
foreach ($data as $k => $v) { foreach ($data as $k => $v) {
if ( is_array( $v ) ) { if ( method_exists($this, $k) ) {
# perhaps should turn into a comma-separated string $this->{$k}($v);
$this->{$k} = implode(',',$v);
} else if ( is_string( $v ) ) {
$this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) {
$this->{$k} = $v;
} else if ( is_bool( $v ) ) {
$this->{$k} = $v;
} else { } else {
Error( "Unknown type $k => $v of var " . gettype( $v ) ); if ( is_array( $v ) ) {
$this->{$k} = $v; # perhaps should turn into a comma-separated string
} $this->{$k} = implode(',',$v);
} } else if ( is_string( $v ) ) {
$this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) {
$this->{$k} = $v;
} else if ( is_bool( $v ) ) {
$this->{$k} = $v;
} else {
Error( "Unknown type $k => $v of var " . gettype( $v ) );
$this->{$k} = $v;
}
} # end if method_exists
} # end foreach $data as $k=>$v
} }
public static function find_all( $parameters = null, $options = null ) { public static function find_all( $parameters = null, $options = null ) {
$filters = array(); $filters = array();
@ -306,7 +312,7 @@ private $control_fields = array(
return $filters; return $filters;
} }
public function save( $new_values = null ) { public function save($new_values = null) {
if ( $new_values ) { if ( $new_values ) {
foreach ( $new_values as $k=>$v ) { foreach ( $new_values as $k=>$v ) {
@ -314,12 +320,12 @@ private $control_fields = array(
} }
} }
$fields = array_keys( $this->defaults ); $fields = array_keys($this->defaults);
$sql = 'UPDATE Monitors SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?'; $sql = 'UPDATE Monitors SET '.implode(', ', array_map(function($field) {return $field.'=?';}, $fields )) . ' WHERE Id=?';
$values = array_map( function($field){return $this->{$field};}, $fields ); $values = array_map(function($field){return $this->{$field};}, $fields);
$values[] = $this->{'Id'}; $values[] = $this->{'Id'};
dbQuery( $sql, $values ); dbQuery($sql, $values);
} // end function save } // end function save
function zmcControl( $mode=false ) { function zmcControl( $mode=false ) {
@ -401,10 +407,20 @@ private $control_fields = array(
} }
} // end if we are on the recording server } // end if we are on the recording server
} }
public function GroupIds( ) { public function GroupIds( $new='') {
if ( $new != '' ) {
if(!is_array($new)) {
$this->{'GroupIds'} = array($new);
} else {
$this->{'GroupIds'} = $new;
}
}
if ( !array_key_exists('GroupIds', $this) ) { if ( !array_key_exists('GroupIds', $this) ) {
if ( array_key_exists('Id', $this) and $this->{'Id'} ) { if ( array_key_exists('Id', $this) and $this->{'Id'} ) {
$this->{'GroupIds'} = dbFetchAll( 'SELECT GroupId FROM Groups_Monitors WHERE MonitorId=?', 'GroupId', array($this->{'Id'}) ); $this->{'GroupIds'} = dbFetchAll('SELECT GroupId FROM Groups_Monitors WHERE MonitorId=?', 'GroupId', array($this->{'Id'}) );
if ( ! $this->{'GroupIds'} )
$this->{'GroupIds'} = array();
} else { } else {
$this->{'GroupIds'} = array(); $this->{'GroupIds'} = array();
} }

View File

@ -13,7 +13,7 @@ class Storage {
if ( ! $row ) { if ( ! $row ) {
Error("Unable to load Storage record for Id=" . $IdOrRow ); Error("Unable to load Storage record for Id=" . $IdOrRow );
} }
} elseif ( is_array( $IdOrRow ) ) { } else if ( is_array($IdOrRow) ) {
$row = $IdOrRow; $row = $IdOrRow;
} }
} }
@ -21,7 +21,7 @@ class Storage {
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
$this->{$k} = $v; $this->{$k} = $v;
} }
$storage_cache[$IdOrRow] = $this; $storage_cache[$row['Id']] = $this;
} else { } else {
$this->{'Name'} = ''; $this->{'Name'} = '';
$this->{'Path'} = ''; $this->{'Path'} = '';
@ -97,7 +97,7 @@ class Storage {
$fields[] = $field.' IS NULL'; $fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) { } else if ( is_array( $value ) ) {
$func = function(){return '?';}; $func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value; $values += $value;
} else { } else {
@ -105,10 +105,10 @@ class Storage {
$values[] = $value; $values[] = $value;
} }
} }
$sql .= implode(' AND ', $fields ); $sql .= implode(' AND ', $fields);
} }
if ( $options and isset($options['order']) ) { if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order']; $sql .= ' ORDER BY ' . $options['order'];
} }
$result = dbQuery($sql, $values); $result = dbQuery($sql, $values);
if ( $result ) { if ( $result ) {
@ -140,16 +140,17 @@ class Storage {
return $usage; return $usage;
} }
public function disk_total_space() { public function disk_total_space() {
if ( ! array_key_exists( 'disk_total_space', $this ) ) { if ( ! array_key_exists('disk_total_space', $this) ) {
$this->{'disk_total_space'} = disk_total_space( $this->Path() ); $this->{'disk_total_space'} = disk_total_space($this->Path());
} }
return $this->{'disk_total_space'}; return $this->{'disk_total_space'};
} }
public function disk_used_space() { public function disk_used_space() {
# This isn't a function like this in php, so we have to add up the space used in each event. # This isn't a function like this in php, so we have to add up the space used in each event.
if ( (! array_key_exists( 'DiskSpace', $this )) or (!$this->{'DiskSpace'}) ) { if ( (! array_key_exists('disk_used_space', $this)) or (!$this->{'disk_used_space'}) ) {
$used = 0;
if ( $this->{'Type'} == 's3fs' ) { if ( $this->{'Type'} == 's3fs' ) {
<<<<<<< HEAD
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) ); $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) );
if ( 0 ) { if ( 0 ) {
foreach ( Event::find_all( array( 'StorageId'=>$this->Id(), 'DiskSpace'=>null ) ) as $Event ) { foreach ( Event::find_all( array( 'StorageId'=>$this->Id(), 'DiskSpace'=>null ) ) as $Event ) {
@ -157,14 +158,35 @@ if ( 0 ) {
$used += $Event->DiskSpace(); $used += $Event->DiskSpace();
} }
} }
=======
$this->{'disk_used_space'} = $this->disk_event_space();
>>>>>>> storageareas
} else { } else {
$path = $this->Path(); $path = $this->Path();
$used = disk_total_space( $path ) - disk_free_space( $path );; $this->{'disk_used_space'} = disk_total_space($path) - disk_free_space($path);
}
}
return $this->{'disk_used_space'};
} // end function disk_used_space
public function event_disk_space() {
# This isn't a function like this in php, so we have to add up the space used in each event.
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) {
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) );
foreach ( Event::find_all( array('StorageId'=>$this->Id(), 'DiskSpace'=>null) ) as $Event ) {
$Event->Storage( $this ); // Prevent further db hit
$used += $Event->DiskSpace();
} }
$this->{'DiskSpace'} = $used; $this->{'DiskSpace'} = $used;
} }
return $this->{'DiskSpace'}; return $this->{'DiskSpace'};
} // end function event_disk_space
public function Server() {
if ( ! array_key_exists('Server',$this) ) {
$this->{'Server'}= new Server( $this->{'ServerId'} );
}
return $this->{'Server'};
} }
} }
?> ?>

View File

@ -145,7 +145,7 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're
} }
// Event scope actions, view permissions only required // Event scope actions, view permissions only required
if ( canView( 'Events' ) ) { if ( canView('Events') ) {
if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) { if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( $action == 'addterm' ) { if ( $action == 'addterm' ) {
@ -155,7 +155,7 @@ if ( canView( 'Events' ) ) {
} else if ( canEdit( 'Events' ) ) { } else if ( canEdit( 'Events' ) ) {
if ( $action == 'delete' ) { if ( $action == 'delete' ) {
if ( ! empty($_REQUEST['Id']) ) { if ( ! empty($_REQUEST['Id']) ) {
dbQuery( 'DELETE FROM Filters WHERE Id=?', array( $_REQUEST['Id'] ) ); dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id']));
} }
} else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) { } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) {
# or ( $action == 'submit' ) ) { # or ( $action == 'submit' ) ) {
@ -189,9 +189,9 @@ if ( canView( 'Events' ) ) {
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0); $sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) { if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
dbQuery( 'UPDATE Filters SET ' . $sql. ' WHERE Id=?', array($_REQUEST['Id']) ); dbQuery('UPDATE Filters SET ' . $sql. ' WHERE Id=?', array($_REQUEST['Id']));
} else { } else {
dbQuery( 'INSERT INTO Filters SET' . $sql ); dbQuery('INSERT INTO Filters SET' . $sql);
$_REQUEST['Id'] = dbInsertId(); $_REQUEST['Id'] = dbInsertId();
} }
if ( $action == 'execute' ) { if ( $action == 'execute' ) {
@ -207,7 +207,7 @@ if ( canView( 'Events' ) ) {
// Event scope actions, edit permissions required // Event scope actions, edit permissions required
if ( canEdit('Events') ) { if ( canEdit('Events') ) {
if ( ($action == 'rename') && isset($_REQUEST['eventName']) && !empty($_REQUEST['eid']) ) { if ( ($action == 'rename') && isset($_REQUEST['eventName']) && !empty($_REQUEST['eid']) ) {
dbQuery( 'UPDATE Events SET Name=? WHERE Id=?', array( $_REQUEST['eventName'], $_REQUEST['eid'] ) ); dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
} else if ( $action == 'eventdetail' ) { } else if ( $action == 'eventdetail' ) {
if ( !empty($_REQUEST['eid']) ) { if ( !empty($_REQUEST['eid']) ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) ); dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) );
@ -299,10 +299,12 @@ if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
continue; continue;
} }
$Monitor = new Monitor( $mid ); $Monitor = new Monitor( $mid );
$Monitor->zmaControl('stop'); if ( $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('stop'); $Monitor->zmaControl('stop');
$Monitor->zmcControl('stop');
}
$Monitor->save( $_REQUEST['newMonitor'] ); $Monitor->save( $_REQUEST['newMonitor'] );
if ($Monitor->Function() != 'None' ) { if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('start'); $Monitor->zmcControl('start');
if ( $Monitor->Enabled() ) { if ( $Monitor->Enabled() ) {
$Monitor->zmaControl('start'); $Monitor->zmaControl('start');
@ -330,7 +332,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$monitor['Function'] = $newFunction; $monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled; $monitor['Enabled'] = $newEnabled;
if ( daemonCheck() ) { if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl( $monitor, 'stop' ); zmaControl( $monitor, 'stop' );
zmcControl( $monitor, $restart?'restart':'' ); zmcControl( $monitor, $restart?'restart':'' );
@ -371,7 +373,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} else { } else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) ); dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
} }
if ( daemonCheck() ) { if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) { if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl( $monitor, 'stop' ); zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'restart' ); zmcControl( $monitor, 'restart' );
@ -399,7 +401,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} }
} }
if($changes>0) { if($changes>0) {
if ( daemonCheck() ) { if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
zmaControl( $mid, 'restart' ); zmaControl( $mid, 'restart' );
} }
$refreshParent = true; $refreshParent = true;
@ -424,7 +426,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$deletedZid = 1; $deletedZid = 1;
} }
if ( $deletedZid ) { if ( $deletedZid ) {
if ( daemonCheck() ) { if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $zone['Type'] == 'Privacy' ) { if ( $zone['Type'] == 'Privacy' ) {
zmaControl( $mid, 'stop' ); zmaControl( $mid, 'stop' );
zmcControl( $mid, 'restart' ); zmcControl( $mid, 'restart' );
@ -458,7 +460,7 @@ if ( canEdit( 'Monitors' ) ) {
$x10Monitor = array(); $x10Monitor = array();
} }
} }
$Monitor = new Monitor( $monitor ); $Monitor = new Monitor($monitor);
// Define a field type for anything that's not simple text equivalent // Define a field type for anything that's not simple text equivalent
$types = array( $types = array(
@ -475,7 +477,7 @@ if ( canEdit( 'Monitors' ) ) {
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
Logger::Debug("Auto selecting server"); Logger::Debug("Auto selecting server");
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne( 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id' ); $_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] );
if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
@ -485,15 +487,17 @@ if ( canEdit( 'Monitors' ) ) {
Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']); Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']);
} }
$columns = getTableColumns( 'Monitors' ); $columns = getTableColumns('Monitors');
$changes = getFormChanges( $monitor, $_REQUEST['newMonitor'], $types, $columns ); $changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
if ( count( $changes ) ) { if ( count( $changes ) ) {
if ( $mid ) { if ( $mid ) {
# If we change anything that changes the shared mem size, zma can complain. So let's stop first. # If we change anything that changes the shared mem size, zma can complain. So let's stop first.
zmaControl( $monitor, 'stop' ); if ( $monitor['Type'] != 'WebSite' ) {
zmcControl( $monitor, 'stop' ); zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'stop' );
}
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
// Groups will be added below // Groups will be added below
if ( isset($changes['Name']) or isset($changes['StorageId']) ) { if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
@ -564,7 +568,16 @@ if ( canEdit( 'Monitors' ) ) {
Error("Users with Monitors restrictions cannot create new monitors."); Error("Users with Monitors restrictions cannot create new monitors.");
return; return;
} }
if ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) or array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds() ) ) {
$restart = true;
} # end if count(changes)
if (
( !isset($_POST['newMonitor']['GroupIds']) )
or
( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) )
or
array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds())
) {
if ( $Monitor->Id() ) if ( $Monitor->Id() )
dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid)); dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid));
@ -574,8 +587,6 @@ if ( canEdit( 'Monitors' ) ) {
} }
} }
} // end if there has been a change of groups } // end if there has been a change of groups
$restart = true;
} # end if count(changes)
if ( ZM_OPT_X10 ) { if ( ZM_OPT_X10 ) {
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] ); $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
@ -599,8 +610,10 @@ if ( canEdit( 'Monitors' ) ) {
$new_monitor = new Monitor($mid); $new_monitor = new Monitor($mid);
//fixDevices(); //fixDevices();
$new_monitor->zmcControl('start'); if ( $monitor['Type'] != 'WebSite' ) {
$new_monitor->zmaControl('start'); $new_monitor->zmcControl('start');
$new_monitor->zmaControl('start');
}
if ( $new_monitor->Controllable() ) { if ( $new_monitor->Controllable() ) {
require_once( 'control_functions.php' ); require_once( 'control_functions.php' );

View File

@ -26,6 +26,7 @@ define( 'ZM_CONFIG_SUBDIR', '@ZM_CONFIG_SUBDIR@' ); // Path to config subfolder
// Define, and override any given in config file // Define, and override any given in config file
define( 'ZM_VERSION', '@VERSION@' ); // Version define( 'ZM_VERSION', '@VERSION@' ); // Version
define( 'ZM_DIR_TEMP', '@ZM_TMPDIR@' ); define( 'ZM_DIR_TEMP', '@ZM_TMPDIR@' );
define( 'ZM_DIR_CACHE', '@ZM_CACHEDIR@' );
global $configvals; global $configvals;
$configFile = ZM_CONFIG; $configFile = ZM_CONFIG;

View File

@ -133,10 +133,7 @@ function dbQuery( $sql, $params=NULL ) {
} }
} else { } else {
if ( defined('ZM_DB_DEBUG') ) { if ( defined('ZM_DB_DEBUG') ) {
if ( $params ) Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'') );
Warning("SQL: $sql" . implode(',',$params) );
else
Warning("SQL: $sql:" );
} }
$result = $dbConn->query($sql); $result = $dbConn->query($sql);
} }
@ -164,16 +161,16 @@ function dbFetchOne( $sql, $col=false, $params=NULL ) {
return false; return false;
} }
if ( $result && $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) { if ( $result && $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) {
if ( $col ) { if ( $col ) {
if ( ! isset( $dbRow[$col] ) ) { if ( ! array_key_exists($col, $dbRow) ) {
Warning( "$col does not exist in the returned row " . print_r($dbRow, true) ); Warning("$col does not exist in the returned row " . print_r($dbRow, true));
} }
return $dbRow[$col]; return $dbRow[$col];
} }
return $dbRow; return $dbRow;
} }
return( false ); return false;
} }
function dbFetchAll( $sql, $col=false, $params=NULL ) { function dbFetchAll( $sql, $col=false, $params=NULL ) {
@ -186,7 +183,7 @@ function dbFetchAll( $sql, $col=false, $params=NULL ) {
$dbRows = array(); $dbRows = array();
while( $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) while( $dbRow = $result->fetch( PDO::FETCH_ASSOC ) )
$dbRows[] = $col?$dbRow[$col]:$dbRow; $dbRows[] = $col?$dbRow[$col]:$dbRow;
return( $dbRows ); return $dbRows;
} }
function dbFetchAssoc( $sql, $indexCol, $dataCol=false ) { function dbFetchAssoc( $sql, $indexCol, $dataCol=false ) {

Some files were not shown because too many files have changed in this diff Show More