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"]
path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git
url = https://github.com/ZoneMinder/crud.git
branch = 3.0
[submodule "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
- qemu
- qemu-user-static
- dnsutils
- traceroute
install:
- update-binfmts --enable qemu-arm
@ -33,8 +35,6 @@ env:
global:
- SMPFLAGS=-j4
matrix:
- OS=el DIST=6
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
- OS=el DIST=7
- OS=fedora DIST=26 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")
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")
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
"Location of dynamic content (events and images), default: /var/lib/zoneminder")
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
installed outside Perl's default search path.")
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 to ON to force building ZM with systemd support. default: OFF")

View File

@ -1,7 +1,7 @@
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

View File

@ -2,6 +2,7 @@
# Create files from the .in files
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
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(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(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install triggers.sql
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` (
`Id` int(10) unsigned NOT NULL auto_increment,
`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,
`CanWake` 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',
`DefaultRate` 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',
`WebColour` varchar(32) NOT NULL default 'red',
`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',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
`CaptureBandwidth` INT NOT NULL default 0,
PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY;
--
@ -714,7 +716,7 @@ CREATE TABLE `Storage` (
--
-- 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 */;
/*!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"} }' );
-- 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
--

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"))
# Configure the zoneminder service files
if(ZM_TARGET_DISTRO STREQUAL "el6")
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY)
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
else(ZM_TARGET_DISTRO STREQUAL "el6")
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
if(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
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")
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")
endif(ZM_WEB_USER STREQUAL "nginx")
# Unpack jscalendar & move files into position
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 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/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 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
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(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.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
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)
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")
if(ZM_TARGET_DISTRO STREQUAL "el6")
install(FILES zoneminder.sysvinit DESTINATION /etc/rc.d/init.d RENAME zoneminder 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")
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)

View File

@ -9,6 +9,23 @@ RewriteEngine On
RewriteCond %{HTTPS} !=on
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@"
<Directory "@ZM_WEBDIR@">
# explicitly set index.php as the only directoryindex

View File

@ -1,26 +1,8 @@
What's New
==========
1. 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
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.
1. See the ZoneMinder release notes for a list of new features:
https://github.com/ZoneMinder/zoneminder/releases
New installs
============
@ -82,16 +64,24 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot.
6. 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:
6. Configure the web server
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.
Inspect the web server configuration file and verify it meets your needs:
/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
If this does not meet your needs, then read README.https to
learn about alternatives. When in doubt, install mod_ssl.
7. Now start the web server:
sudo systemctl enable httpd
@ -102,14 +92,34 @@ New installs
sudo systemctl enable zoneminder
sudo systemctl start zoneminder
9. The Fedora repos have a ZoneMinder package available, but it does not
support ffmpeg or libvlc, which many modern IP cameras require. Most users
will want to prevent the ZoneMinder package in the Fedora repos from
overwriting the ZoneMinder package in zmrepo, during a future dnf update. To
prevent that from happening you must edit /etc/yum.repos.d/fedora.repo
and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*"
without the quotes under the [fedora] and [fedora-updates] blocks,
respectively.
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
========
@ -131,7 +141,7 @@ Upgrades
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
/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
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.

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
==========
1. 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
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.
1. See the ZoneMinder release notes for a list of new features:
https://github.com/ZoneMinder/zoneminder/releases
New installs
============
@ -82,15 +64,23 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot.
6. 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:
6. Configure the web server
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
learn about alternatives. When in doubt, install mod_ssl.
Inspect the web server configuration file and verify it meets your needs:
/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:
@ -102,6 +92,35 @@ New installs
sudo systemctl enable 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
========
@ -122,7 +141,7 @@ Upgrades
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
/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
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.

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
be installed along with ZoneMinder. Upon installation, the mod_ssl package
will create a default, self-signed certificate. This is the certificate that
ZoneMinder will use out of the box.
By default, ZoneMinder will use the certifciate created when the mod_ssl
pacakge was installed on your system.
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.
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,
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
"centos certificate" reveals many articles on the subject. Note that some
third party applications, such as zmNinja, will require you to create a
certificate different than the default certificate on your machine.
"centos certificate" reveals many articles on the subject.
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
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_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
# 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"
%global with_nginx 1
%global wwwconfdir %{_sysconfdir}/nginx/default.d
%else
%global wwwconfdir %{_sysconfdir}/httpd/conf.d
%endif
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
@ -22,18 +22,11 @@
%global with_apcu_bc 1
%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 _hardened_build 1
Name: zoneminder
Version: 1.31.1
Version: 1.31.43
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@ -41,18 +34,18 @@ Group: System Environment/Daemons
# Mootools is inder the MIT license: http://mootools.net/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# 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
URL: http://www.zoneminder.com/
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}
%{?with_init_systemd:BuildRequires: mariadb-devel}
%{?with_init_systemd:BuildRequires: perl-podlators}
%{?with_init_systemd:BuildRequires: polkit-devel}
%{?with_init_sysv:BuildRequires: mysql-devel}
%{?el6:BuildRequires: epel-rpm-macros}
BuildRequires: systemd-devel
BuildRequires: mariadb-devel
BuildRequires: perl-podlators
BuildRequires: polkit-devel
BuildRequires: cmake >= 2.8.7
BuildRequires: gnutls-devel
BuildRequires: bzip2-devel
@ -82,6 +75,7 @@ BuildRequires: vlc-devel
BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel
BuildRequires: desktop-file-utils
# Required for mp4 container support
BuildRequires: libmp4v2-devel
@ -116,19 +110,10 @@ Requires: perl(LWP::Protocol::https)
Requires: ca-certificates
Requires: zip
%{?with_init_systemd:Requires(post): systemd}
%{?with_init_systemd:Requires(post): systemd-sysv}
%{?with_init_systemd:Requires(preun): systemd}
%{?with_init_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): systemd
Requires(post): systemd-sysv
Requires(preun): systemd
Requires(postun): systemd
Requires(post): %{_bindir}/gpasswd
Requires(post): %{_bindir}/less
@ -147,6 +132,11 @@ too much degradation of performance.
%{__rm} -rf ./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
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload
@ -167,6 +157,12 @@ too much degradation of performance.
%install
%make_install
desktop-file-install \
--dir %{buildroot}%{_datadir}/applications \
--delete-original \
--mode 644 \
%{buildroot}%{_datadir}/applications/zoneminder.desktop
# 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 || :
@ -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
%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
if [ $1 -eq 1 ] ; then
%systemd_post %{name}.service
fi
%endif
# Upgrade from a previous version of zoneminder
if [ $1 -eq 2 ] ; then
@ -249,34 +231,11 @@ EOF
%endif
%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
%endif
%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
%endif
%if 0%{?with_init_systemd}
%triggerun -- zoneminder < 1.25.0-4
# Save the current service runlevel info
# 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
/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || :
/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || :
%endif
%files
%license COPYING
@ -304,25 +262,18 @@ rm -rf %{_docdir}/%{name}-%{version}
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.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
%if 0%{?with_nginx}
%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf
%endif
%if 0%{?with_init_systemd}
%{_tmpfilesdir}/zoneminder.conf
%{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%{_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}/zmaudit.pl
@ -341,6 +292,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_bindir}/zmtelemetry.pl
%{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl
%{_bindir}/zmstats.pl
%{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/ONVIF*
@ -351,6 +303,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_libexecdir}/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/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/swap
%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}/spool/zoneminder-upload
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%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
- modify autosetup macro parameters
- 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
</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
<Directory /usr/share/zoneminder/www>
php_flag register_globals off
@ -15,6 +21,28 @@ Alias /zm /usr/share/zoneminder/www
</IfModule>
</Directory>
<Directory /usr/share/zoneminder/www/api>
AllowOverride All
# 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 "/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>

View File

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

View File

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

View File

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

View File

@ -6,6 +6,12 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted
</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
<Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks
@ -14,6 +20,27 @@ Alias /zm /usr/share/zoneminder/www
</IfModule>
</Directory>
<Directory /usr/share/zoneminder/www/api>
AllowOverride All
# 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 "/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>

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ if [ "$1" = "configure" ]; then
chown www-data:root /var/log/zm
chown www-data:www-data /var/lib/zm
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
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."
@ -35,6 +35,7 @@ if [ "$1" = "configure" ]; then
else
DBSERVICE="mysql.service"
fi
echo "Detected db service is $DBSERVICE"
if systemctl is-failed --quiet $DBSERVICE; then
echo "$DBSERVICE is in a failed state; it will not be started."
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
# test if database if already present...
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
# 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
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
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
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi

View File

@ -1,4 +1,4 @@
d /var/run/zm 0755 www-data www-data
d /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
-------------------------
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:
::
@ -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.
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
-----------------------------------------

View File

@ -147,6 +147,23 @@ Keep aspect ratio
Orientation
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
-------------

View File

@ -8,6 +8,23 @@
ServerAdmin webmaster@localhost
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@"
<Directory "@WEB_PREFIX@">
Options -Indexes +FollowSymLinks
@ -38,6 +55,31 @@
</IfModule>
</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
# 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)

View File

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

View File

@ -1723,26 +1723,6 @@ our @options = (
type => $types{abs_path},
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',
default => 'yes',
@ -2966,6 +2946,23 @@ our @options = (
type => $types{boolean},
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',
default => '60',

View File

@ -397,7 +397,7 @@ sub delete {
if ( (! $Config{ZM_OPT_FAST_DELETE}) and $event->Storage()->DoDelete() ) {
$event->delete_files( );
} else {
Debug('Not deleting frames, stats and files for speed.');
Debug('Not deleting event files from '.$event->Path().' for speed.');
}
} # end sub delete
@ -519,6 +519,22 @@ sub DiskSpace {
sub MoveTo {
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();
$self->lock_and_load();
# data is reloaded, so need to check that the move hasn't already happened.
@ -526,25 +542,13 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit();
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;
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
if ( $NewPath eq $OldPath ) {
$ZoneMinder::Database::dbh->commit();

View File

@ -160,9 +160,12 @@ sub Sql {
if ( $term->{attr} =~ /^Monitor/ ) {
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$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};
} elsif ( $term->{attr} eq 'FilterServerId' ) {
$self->{Sql} .= $Config{ZM_SERVER_ID};
# StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= 'E.StartTime';
@ -172,7 +175,7 @@ sub Sql {
$self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'StartDate' ) {
$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 )';
} elsif ( $term->{attr} eq 'Weekday' ) {
$self->{Sql} .= 'weekday( E.StartTime )';
@ -208,7 +211,7 @@ sub Sql {
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $term->{attr} =~ /^MonitorName/ ) {
$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})");
if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";

View File

@ -147,7 +147,7 @@ our $mem_data = {
last_write_index => { type=>'uint32', seq=>$mem_seq++ },
last_read_index => { 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++ },
brightness => { 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++ },
control_state => { type=>'uint8[256]', seq=>$mem_seq++ },
alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ },
}
},
trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> {

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
CpuLoad
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -107,6 +108,17 @@ sub Hostname {
return $_[0]{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;
__END__
# Below is stub documentation for your module. You'd better edit it!

View File

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

View File

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

View File

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

View File

@ -283,6 +283,8 @@ sub checkFilter {
foreach my $event ( @Events ) {
last if $zm_terminate;
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
Debug( "Checking event $event->{Id}" );
my $delete_ok = !undef;
$dbh->ping();
@ -302,7 +304,7 @@ sub checkFilter {
}
if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) {
if ( !$event->{Emailed} ) {
$delete_ok = undef if ( !sendEmail( $filter, $event ) );
$delete_ok = undef if ( !sendEmail( $filter, $Event ) );
}
}
if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) {
@ -322,7 +324,6 @@ sub checkFilter {
}
if ( $filter->{AutoDelete} ) {
if ( $delete_ok ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$Event->delete();
} else {
Error( "Unable toto delete event $event->{Id} as previous operations failed\n" );
@ -330,19 +331,23 @@ sub checkFilter {
} # end if AutoDelete
if ( $filter->{AutoMove} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );
$_ = $Event->MoveTo( $NewStorage );
Error($_) if $_;
}
if ( $filter->{UpdateDiskSpace} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load();
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();
}
$ZoneMinder::Database::dbh->commit();
@ -393,25 +398,25 @@ sub generateVideo {
chomp( $output );
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug( "Output: $output\n" );
Debug("Output: $output\n");
}
if ( $status ) {
Error( "Video generation '$command' failed with status: $status\n" );
Error("Video generation '$command' failed with status: $status\n");
if ( wantarray() ) {
return( undef, undef );
}
return( 0 );
return 0;
} else {
my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?';
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} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
or Fatal("Unable toexecute '$sql': ".$sth->errstr());
if ( wantarray() ) {
return( $format, $output );
}
}
return( 1 );
return 1;
}
# 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
# An empty string is returned if no one from methods above works
sub generateImage {
my $event = shift;
my $Event = 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 $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', getEventPath($event), $frame->{FrameId}) if $analyse;
my $video_path = sprintf('%s/%d-video.mp4', getEventPath($event), $event->{Id});
my $event_path = $Event->Path();
my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', $event_path, $frame->{FrameId});
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 = '';
# 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;
} elsif ( -r $capture_image_path ) {
$image_path = $capture_image_path;
} elsif ( -e $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) ) {
} elsif ( -r $video_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;
}
}
@ -581,7 +597,7 @@ sub uploadArchFile {
sub substituteTags {
my $text = shift;
my $filter = shift;
my $event = shift;
my $Event = shift;
my $attachments_ref = shift;
# First we'd better check what we need to get
@ -589,35 +605,7 @@ sub substituteTags {
# monitor information?
my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
my $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 );
}
my $Monitor = $Event->Monitor() if $need_monitor;
# Do we need the image information too?
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/;
@ -631,8 +619,9 @@ sub substituteTags {
;
my $sth = $dbh->prepare_cached( $sql )
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() );
my $rows = 0;
while( my $frame = $sth->fetchrow_hashref() ) {
if ( !$first_alarm_frame ) {
$first_alarm_frame = $frame;
@ -641,62 +630,68 @@ sub substituteTags {
$max_alarm_frame = $frame;
$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();
}
my $url = $Config{ZM_URL};
$text =~ s/%ZP%/$url/g;
$text =~ s/%MN%/$event->{MonitorName}/g;
$text =~ s/%MET%/$monitor->{EventCount}/g;
$text =~ s/%MEH%/$monitor->{HourEventCount}/g;
$text =~ s/%MED%/$monitor->{DayEventCount}/g;
$text =~ s/%MEW%/$monitor->{WeekEventCount}/g;
$text =~ s/%MEM%/$monitor->{MonthEventCount}/g;
$text =~ s/%MEA%/$monitor->{ArchEventCount}/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/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/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/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g;
$text =~ s/%EI%/$event->{Id}/g;
$text =~ s/%EN%/$event->{Name}/g;
$text =~ s/%EC%/$event->{Cause}/g;
$text =~ s/%ED%/$event->{Notes}/g;
$text =~ s/%ET%/$event->{StartTime}/g;
$text =~ s/%EL%/$event->{Length}/g;
$text =~ s/%EF%/$event->{Frames}/g;
$text =~ s/%EFA%/$event->{AlarmFrames}/g;
$text =~ s/%EST%/$event->{TotScore}/g;
$text =~ s/%ESA%/$event->{AvgScore}/g;
$text =~ s/%ESM%/$event->{MaxScore}/g;
$text =~ s/%MN%/$Event->{MonitorName}/g;
$text =~ s/%MET%/$Monitor->{TotalEvents}/g;
$text =~ s/%MEH%/$Monitor->{HourEvents}/g;
$text =~ s/%MED%/$Monitor->{DayEvents}/g;
$text =~ s/%MEW%/$Monitor->{WeekEvents}/g;
$text =~ s/%MEM%/$Monitor->{MonthEvents}/g;
$text =~ s/%MEA%/$Monitor->{ArchivedEvents}/g;
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/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/%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/%EI%/$Event->{Id}/g;
$text =~ s/%EN%/$Event->{Name}/g;
$text =~ s/%EC%/$Event->{Cause}/g;
$text =~ s/%ED%/$Event->{Notes}/g;
$text =~ s/%ET%/$Event->{StartTime}/g;
$text =~ s/%EL%/$Event->{Length}/g;
$text =~ s/%EF%/$Event->{Frames}/g;
$text =~ s/%EFA%/$Event->{AlarmFrames}/g;
$text =~ s/%EST%/$Event->{TotScore}/g;
$text =~ s/%ESA%/$Event->{AvgScore}/g;
$text =~ s/%ESM%/$Event->{MaxScore}/g;
if ( $first_alarm_frame ) {
$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/%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;
if ( $attachments_ref && $text =~ s/%EI1%//g ) {
my $path = generateImage( $event, $first_alarm_frame );
my $path = generateImage($Event, $first_alarm_frame);
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
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 ) {
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 ) {
my $path = generateImage( $event, $first_alarm_frame, 'analyse' );
my $path = generateImage($Event, $first_alarm_frame, 'analyse');
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 ) {
@ -704,9 +699,11 @@ sub substituteTags {
if ( !@$attachments_ref
|| ($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 ) {
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 ( $text =~ s/%EV%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event );
my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) {
return( undef );
return undef;
}
push( @$attachments_ref, { type=>"video/$format", path=>$path } );
}
if ( $text =~ s/%EVM%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event, 1 );
my ( $format, $path ) = generateVideo($filter, $Event, 1);
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;
( 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
sub sendEmail {
my $filter = shift;
my $event = shift;
my $Event = shift;
if ( ! $Config{ZM_FROM_EMAIL} ) {
Error( "No 'from' email address defined, not sending email" );
return( 0 );
Error("No 'from' email address defined, not sending email");
return 0;
}
if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
Error( 'No email address defined, not sending email' );
return( 0 );
Error('No email address defined, not sending email');
return 0;
}
Info( "Creating notification email\n" );
Info("Creating notification email\n");
my $subject = substituteTags( $Config{ZM_EMAIL_SUBJECT}, $filter, $event );
return( 0 ) if ( !$subject );
my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
return 0 if !$subject;
my @attachments;
my $body = substituteTags( $Config{ZM_EMAIL_BODY}, $filter, $event, \@attachments );
return( 0 ) if ( !$body );
my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
return 0 if !$body;
Info( "Sending notification email '$subject'\n" );
Info("Sending notification email '$subject'\n");
eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
@ -829,9 +826,9 @@ sub sendEmail {
Info( "Notification email sent\n" );
}
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() );
my $res = $sth->execute( $event->{Id} )
my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
return( 1 );

View File

@ -211,7 +211,7 @@ if ( $command =~ /^(?:start|restart)$/ ) {
my $res = $sth->execute( @values )
or Fatal( "Can't execute: ".$sth->errstr() );
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' ) {
runCommand( "zmdc.pl start zmc -d $monitor->{Device}" );
} else {

View File

@ -84,6 +84,7 @@ while( 1 ) {
while( my $monitor = $sth->fetchrow_hashref() ) {
my $now = time();
next if $monitor->{Function} eq 'None';
next if $monitor->{Type} eq 'WebSite';
my $restart = 0;
if ( zmMemVerify( $monitor ) ) {
# Check we have got an image recently
@ -147,11 +148,11 @@ while( 1 ) {
if ( !defined($image_time) ) {
# Can't read from shared data
$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 ) {
# We can't get the last capture time so can't be sure it's died.
$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 {
my $max_image_delay = ( $monitor->{MaxFPS}

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
#include <getopt.h>
#include <arpa/inet.h>
#include <glob.h>
#include <cinttypes>
#include "zm.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]);
}
// 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 )
Error("Can't symlink %s -> %s: %s", id_file.c_str(), path.c_str(), strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) {
@ -161,7 +162,7 @@ Event::Event(
if ( errno != EEXIST )
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) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
@ -169,7 +170,7 @@ Event::Event(
}
} else {
// 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 ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
@ -177,7 +178,7 @@ Event::Event(
}
// 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") )
fclose(id_fp);
else
@ -238,7 +239,7 @@ Event::~Event() {
if ( frames > last_db_frame ) {
Debug(1, "Adding closing frame %d to DB", frames);
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);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
@ -250,7 +251,7 @@ Event::~Event() {
}
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 );
db_mutex.lock();
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());
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();
if ( mysql_query( &dbconn, sql ) ) {
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);
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++;
}
@ -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]);
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();
if ( mysql_query(&dbconn, sql) ) {
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
if ( frame_type == BULK ) {
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.sec, delta_time.fsec,
frames,

View File

@ -75,7 +75,7 @@ class Event {
static int pre_alarm_count;
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
unsigned long long int id;
uint64_t id;
Monitor *monitor;
struct timeval start_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();
unsigned long long int Id() const { return id; }
uint64_t Id() const { return id; }
const std::string &Cause() { return cause; }
int Frames() const { return frames; }
int AlarmFrames() const { return alarm_frames; }

View File

@ -27,6 +27,7 @@
#include <getopt.h>
#include <arpa/inet.h>
#include <glob.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"
@ -62,7 +63,7 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
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);
@ -87,7 +88,7 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
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);
if ( init_frame_id ) {
@ -105,10 +106,10 @@ bool EventStream::loadInitialEventData( unsigned long long init_event_id, unsign
return true;
}
bool EventStream::loadEventData(unsigned long long event_id) {
bool EventStream::loadEventData(uint64_t event_id) {
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) ) {
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 ) {
struct tm *event_time = localtime( &event_data->start_time );
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 );
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,
event_data->event_id );
} else {
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 );
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 );
}
delete storage; storage = NULL;
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) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
@ -254,7 +255,7 @@ bool EventStream::loadEventData(unsigned long long event_id) {
else
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;
} // bool EventStream::loadEventData( int event_id )
@ -450,7 +451,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
break;
}
struct {
unsigned long long event_id;
uint64_t event_id;
int progress;
int rate;
int zoom;
@ -462,7 +463,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
status_data.rate = replay_rate;
status_data.zoom = zoom;
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.paused,
status_data.progress,
@ -492,10 +493,10 @@ void EventStream::checkEventLoaded() {
static char sql[ZM_SQL_SML_BUFSIZ];
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;
} 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;
}
@ -520,8 +521,8 @@ void EventStream::checkEventLoaded() {
}
if ( dbrow ) {
unsigned long long event_id = atoll(dbrow[0]);
Debug( 1, "Loading new event %llu", event_id );
uint64_t event_id = atoll(dbrow[0]);
Debug( 1, "Loading new event %" PRIu64, event_id );
loadEventData(event_id);
@ -846,7 +847,7 @@ void EventStream::runStream() {
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 );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
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 {
unsigned long long event_id;
uint64_t event_id;
unsigned long monitor_id;
unsigned long storage_id;
unsigned long frame_count;
@ -83,8 +83,8 @@ class EventStream : public StreamBase {
FFmpeg_Input *ffmpeg_input;
protected:
bool loadEventData( unsigned long long event_id );
bool loadInitialEventData( unsigned long long init_event_id, unsigned int init_frame_id );
bool loadEventData( uint64_t event_id );
bool loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id );
bool loadInitialEventData( int monitor_id, time_t event_time );
void checkEventLoaded();
@ -110,7 +110,7 @@ class EventStream : public StreamBase {
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 setStreamMode( StreamMode p_mode ) {
mode = p_mode;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include <sys/stat.h>
#include <arpa/inet.h>
#include <glob.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"
@ -77,7 +78,7 @@ std::string load_monitor_sql =
"EventPrefix, LabelFormat, LabelX, LabelY, LabelSize,"
"ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, "
"SectionLength, FrameSkip, MotionFrameSkip, "
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, SignalCheckColour FROM Monitors";
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, SignalCheckPoints, SignalCheckColour FROM Monitors";
std::string CameraType_Strings[] = {
"Local",
@ -316,6 +317,7 @@ Monitor::Monitor()
ref_blend_perc(0),
alarm_ref_blend_perc(0),
track_motion(0),
signal_check_points(0),
signal_check_colour(0),
embed_exif(0),
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)
+ 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;
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->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 ),
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
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)));
}
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];
for ( int i = 0; i < image_buffer_count; i++ ) {
image_buffer[i].image_index = i;
@ -887,7 +897,7 @@ Monitor::~Monitor() {
if ( mem_ptr ) {
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 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 {
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,
shared_data->size,
shared_data->state,
@ -1105,7 +1115,7 @@ double Monitor::GetFPS() const {
ZMPacket *snap1 = &image_buffer[index1];
if ( !snap1->timestamp->tv_sec ) {
// 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;
}
struct timeval time1 = *snap1->timestamp;
@ -1196,9 +1206,9 @@ void Monitor::actionReload() {
void Monitor::actionEnable() {
shared_data->action |= RELOAD;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE Monitors SET Enabled = 1 WHERE Id = %d", id);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
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()) ) {
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();
stream->setStreamStart(event_id, (unsigned int)1);
zone_image = stream->getImage();
@ -1446,7 +1456,7 @@ bool Monitor::CheckSignal( const Image *image ) {
static Rgb colour_val; /* RGB32 color */
static int usedsubpixorder;
if ( config.signal_check_points > 0 ) {
if ( signal_check_points > 0 ) {
if ( static_undef ) {
static_undef = false;
usedsubpixorder = camera->SubpixelOrder();
@ -1464,8 +1474,9 @@ bool Monitor::CheckSignal( const Image *image ) {
int colours = image->Colours();
int index = 0;
for ( int i = 0; i < config.signal_check_points; i++ ) {
for ( int i = 0; i < signal_check_points; i++ ) {
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);
if ( !config.timestamp_on_capture || !label_format[0] )
break;
@ -1501,6 +1512,7 @@ bool Monitor::CheckSignal( const Image *image ) {
}
}
} // end for < signal_check_points
Debug(1,"SignalCheck: %d points, colour_val(%d)", signal_check_points, colour_val);
return false;
} // end if signal_check_points
return true;
@ -1650,7 +1662,7 @@ bool Monitor::Analyse() {
if ( !signal ) {
signalText = "Lost";
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();
last_section_mod = 0;
}
@ -1757,7 +1769,7 @@ bool Monitor::Analyse() {
alarm_cause = cause+" "+alarm_cause;
strncpy( shared_data->alarm_cause,alarm_cause.c_str() , sizeof(shared_data->alarm_cause) );
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 */
if ( state == IDLE ) {
shared_data->state = state = TAPE;
@ -1798,15 +1810,15 @@ bool Monitor::Analyse() {
last_alarm_count = analysis_image_count;
} else { // no score?
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;
} else if ( state == ALERT ) {
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 != RECORD && function != MOCORD ) || event_close_mode == CLOSE_ALARM ) {
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();
} else {
shared_data->state = state = TAPE;
@ -1881,7 +1893,7 @@ bool Monitor::Analyse() {
} else {
Debug(3, "trigger == off");
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();
}
shared_data->state = state = IDLE;
@ -1935,7 +1947,7 @@ void Monitor::Reload() {
Debug(1, "Reloading monitor %s", name);
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();
}
@ -2001,8 +2013,8 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
if ( dest_ptr != link_id_str ) {
*dest_ptr = '\0';
unsigned int link_id = atoi(link_id_str);
if ( link_id > 0 && link_id != id) {
Debug( 3, "Found linked monitor id %d", link_id );
if ( link_id > 0 && link_id != id ) {
Debug(3, "Found linked monitor id %d", link_id);
int j;
for ( j = 0; j < n_link_ids; j++ ) {
if ( link_ids[j] == link_id )
@ -2022,15 +2034,16 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
break;
}
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];
int count = 0;
for ( int i = 0; i < n_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();
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) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
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]);
}
mysql_free_result(result);
} // end foreach n_link_id
} // end foreach link_id
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) {
@ -2131,7 +2144,6 @@ int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose p
}
#endif // HAVE_LIBAVFORMAT
/* Returns 0 on success, even if no new images are available (transient error)
* Returns -1 on failure.
*/
@ -2285,7 +2297,7 @@ int Monitor::Capture() {
capture_image->Flip( orientation==FLIP_HORI );
break;
}
}
} // end if have rotation
if ( privacy_bitmask )
capture_image->MaskPrivacy( privacy_bitmask );
@ -2294,27 +2306,32 @@ int Monitor::Capture() {
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_time = image_buffer[index].timestamp->tv_sec;
image_count++;
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;
// 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 ) {
// # 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 -> %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 ) {
capture_fps = new_capture_fps;
last_fps_time = now;
static char sql[ZM_SQL_SML_BUFSIZ];
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) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
@ -2330,6 +2347,7 @@ int Monitor::Capture() {
} // end if result
} // end if deinterlacing
// Icon: I'm not sure these should be here. They have nothing to do with capturing
if ( shared_data->action & GET_SETTINGS ) {
shared_data->brightness = camera->Brightness();
@ -2647,6 +2665,7 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture() ; }
int Monitor::Close() { return camera->Close(); };
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.

View File

@ -117,38 +117,38 @@ protected:
uint32_t last_write_index; /* +4 */
uint32_t last_read_index; /* +8 */
uint32_t state; /* +12 */
uint32_t last_event_id; /* +16 */
uint32_t action; /* +20 */
int32_t brightness; /* +24 */
int32_t hue; /* +28 */
int32_t colour; /* +32 */
int32_t contrast; /* +36 */
int32_t alarm_x; /* +40 */
int32_t alarm_y; /* +44 */
uint8_t valid; /* +48 */
uint8_t active; /* +49 */
uint8_t signal; /* +50 */
uint8_t format; /* +51 */
uint32_t imagesize; /* +52 */
uint32_t epadding1; /* +56 */
uint32_t epadding2; /* +60 */
uint64_t last_event_id; /* +16 */
uint32_t action; /* +24 */
int32_t brightness; /* +28 */
int32_t hue; /* +32 */
int32_t colour; /* +36 */
int32_t contrast; /* +40 */
int32_t alarm_x; /* +44 */
int32_t alarm_y; /* +48 */
uint8_t valid; /* +52 */
uint8_t active; /* +53 */
uint8_t signal; /* +54 */
uint8_t format; /* +55 */
uint32_t imagesize; /* +56 */
uint32_t epadding1; /* +60 */
uint32_t epadding2; /* +64 */
/*
** 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.
*/
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 */
uint64_t extrapad1;
};
union { /* +72 */
union { /* +76 */
time_t last_write_time;
uint64_t extrapad2;
};
union { /* +80 */
union { /* +84 */
time_t last_read_time;
uint64_t extrapad3;
};
uint8_t control_state[256]; /* +88 */
uint8_t control_state[256]; /* +92 */
char alarm_cause[256];
@ -300,12 +300,15 @@ protected:
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.
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
bool embed_exif; // Whether to embed Exif data into each image frame or not
double capture_fps; // Current capturing fps
double analysis_fps; // Current analysis fps
unsigned int last_camera_bytes;
Image delta_image;
Image ref_image;
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
@ -339,8 +342,6 @@ protected:
#endif // ZM_MEM_MAPPED
off_t mem_size;
unsigned char *mem_ptr;
Storage *storage;
SharedData *shared_data;
TriggerData *trigger_data;
VideoStoreData *video_store_data;
@ -354,6 +355,7 @@ protected:
Camera *camera;
Event *event;
Storage *storage;
int n_zones;
Zone **zones;
@ -487,6 +489,7 @@ public:
int PreCapture() const;
int Capture();
int PostCapture() const;
int Close();
void CheckAction();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,13 +108,11 @@ 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 ) {
static char diag_path[PATH_MAX] = "";
if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
}
pg_image->WriteJpeg( diag_path );
snprintf(diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
pg_image->WriteJpeg(diag_path);
} else {
diag_path[0] = 0;
}
} // end Zone::Setup
@ -127,21 +125,22 @@ Zone::~Zone() {
void Zone::RecordStats( const Event *event ) {
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();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event stats: %s", mysql_error( &dbconn ) );
snprintf(sql, sizeof(sql),
"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();
} // end void Zone::RecordStats( const Event *event )
bool Zone::CheckOverloadCount() {
Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames);
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--;
return( false );
return false;
}
return true;
} // end bool Zone::CheckOverloadCount()
@ -180,28 +179,27 @@ int Zone::GetExtendAlarmFrames() {
} // end int Zone::GetExtendAlarmFrames()
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 ) {
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--;
return( true );
return true;
}
return false;
} // end bool Zone::CheckExtendAlarmCount
bool Zone::CheckAlarms( const Image *delta_image ) {
bool Zone::CheckAlarms(const Image *delta_image) {
ResetStats();
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 );
Info("In overload mode, %d frames of %d remaining", overload_count, overload_frames);
overload_count--;
return( false );
return false;
}
delete 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();
uint8_t* diff_buff = (uint8_t*)diff_image->Buffer();
uint8_t* pdiff;
@ -221,7 +219,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
unsigned int hi_x = polygon.HiX();
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) {
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);
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
if ( 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( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
if ( alarm_pixels ) {
if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
/* Not enough pixels alarmed */
return (false);
} else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) {
return false;
} else if ( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
return (false);
return false;
}
} else {
/* No alarmed pixels */
return (false);
return false;
}
score = (100*alarm_pixels)/polygon.Area();
if ( score < 1 )
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 ) {
int bx = filter_box.X();
@ -268,7 +261,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
int by1 = by-1;
Debug( 5, "Checking for filtered pixels" );
Debug(5, "Checking for filtered pixels");
if ( bx > 1 || by > 1 ) {
// Now remove any pixels smaller than our filter size
unsigned char *cpdiff;
@ -306,47 +299,42 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
continue;
}
alarm_filter_pixels++;
}
}
}
} // end if white
} // end for x
} // end foreach y line
} else {
alarm_filter_pixels = alarm_pixels;
}
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
if ( config.record_diag_images )
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( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
if ( alarm_filter_pixels ) {
if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
/* Not enough pixels alarmed */
return (false);
} else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) {
return false;
} else if ( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
return (false);
return false;
}
} else {
/* No filtered pixels */
return (false);
return false;
}
score = (100*alarm_filter_pixels)/(polygon.Area());
if ( score < 1 )
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 ) {
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;
BlobStats blob_stats[256];
memset( blob_stats, 0, sizeof(BlobStats)*256 );
memset(blob_stats, 0, sizeof(BlobStats)*256);
uint8_t *spdiff;
uint8_t last_x, last_y;
BlobStats *bsx, *bsy;
@ -358,32 +346,32 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
pdiff = (uint8_t*)diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) {
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_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0;
last_x = 0;
if(x > 0) {
if((x-1) >= lo_x) {
if ( x > 0 ) {
if ( (x-1) >= lo_x ) {
last_x = *(pdiff-1);
}
}
last_y = 0;
if(y > 0) {
if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) {
if (y > 0 ) {
if ( (y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x ) {
last_y = *(pdiff-diff_width);
}
}
if ( last_x ) {
Debug( 9, "Left neighbour is %d", last_x );
Debug(9, "Left neighbour is %d", last_x);
bsx = &blob_stats[last_x];
if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y );
Debug(9, "Top neighbour is %d", last_y);
bsy = &blob_stats[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)
*pdiff = last_x;
alarm_blob_pixels++;
@ -395,22 +383,29 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bsm = bsx->count>=bsy->count?bsx:bsy;
bss = bsm==bsx?bsy:bsx;
Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag );
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 );
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 );
Debug(9,
"Different neighbours, setting pixels of %d to %d\n"
"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
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 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, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x );
Debug(9,
"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);
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 ) {
Debug( 9, "Setting pixel" );
Debug(9, "Setting pixel");
*spdiff = bsm->tag;
changed++;
}
@ -419,10 +414,14 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
*pdiff = bsm->tag;
alarm_blob_pixels++;
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( "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 );
Error( "No pixels changed, exiting" );
exit( -1 );
Info(
"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",
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
@ -436,7 +435,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
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
bss->tag = 0;
@ -447,7 +446,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bss->hi_y = 0;
}
} 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
*pdiff = last_x;
alarm_blob_pixels++;
@ -457,8 +456,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
}
} else {
if ( last_y ) {
Debug( 9, "Top neighbour is %d", last_y );
Debug( 9, "Setting to top neighbour %d", last_y );
Debug(9, "Top neighbour is %d", last_y);
// Add to the blob from the y side
BlobStats *bsy = &blob_stats[last_y];
@ -489,7 +487,8 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
alarm_blobs--;
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->count = 0;
@ -500,7 +499,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
}
}
if ( !bs->count ) {
Debug( 9, "Creating new blob %d", i );
Debug(9, "Creating new blob %d", i);
*pdiff = i;
alarm_blob_pixels++;
bs->tag = i;
@ -509,12 +508,12 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bs->lo_y = bs->hi_y = y;
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;
}
}
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;
y = hi_y+1;
}
@ -523,19 +522,15 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
}
}
}
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
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
for ( int i = 1; i < WHITE; i++ ) {
@ -555,7 +550,8 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
alarm_blobs--;
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->count = 0;
@ -564,39 +560,37 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
bs->hi_x = 0;
bs->hi_y = 0;
} 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 ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count;
}
}
}
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
} // end if bs_count
} // end for i < WHITE
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
if( alarm_blobs ) {
if( min_blobs && (alarm_blobs < min_blobs) ) {
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 ) {
if ( min_blobs && (alarm_blobs < min_blobs) ) {
/* Not enough pixels alarmed */
return (false);
} else if(max_blobs && (alarm_blobs > max_blobs) ) {
return false;
} else if ( max_blobs && (alarm_blobs > max_blobs) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
return (false);
return false;
}
} else {
/* No blobs */
return (false);
return false;
}
score = (100*alarm_blob_pixels)/(polygon.Area());
if ( score < 1 )
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_hi_x = polygon.LoX()-1;
@ -648,15 +642,15 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
score *= 2;
}
Debug( 5, "Adjusted score is %d", score );
Debug(5, "Adjusted score is %d", score);
// Now outline the changed region
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 ( true ) {
alarm_centre = Coord( alarm_mid_x, alarm_mid_y );
alarm_centre = Coord(alarm_mid_x, alarm_mid_y);
} else {
alarm_centre = alarm_box.Centre();
}
@ -698,9 +692,9 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
}
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 {
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
@ -710,18 +704,18 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
image = 0;
}
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 );
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);
}
return( true );
return true;
}
bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
Debug( 3, "Parsing polygon string '%s'", poly_string );
bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) {
Debug(3, "Parsing polygon string '%s'", poly_string);
char *str_ptr = new char[strlen(poly_string)+1];
char *str = str_ptr;
strcpy( str, poly_string );
strcpy(str, poly_string);
char *ws;
int n_coords = 0;
@ -731,16 +725,16 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
if ( *str == '\0' ) {
break;
}
ws = strchr( str, ' ' );
ws = strchr(str, ' ');
if ( ws ) {
*ws = '\0';
}
char *cp = strchr( str, ',' );
char *cp = strchr(str, ',');
if ( !cp ) {
Error( "Bogus coordinate %s found in polygon string", str );
Error("Bogus coordinate %s found in polygon string", str);
delete[] coords;
delete[] str_ptr;
return( false );
return false;
} else {
*cp = '\0';
char *xp = str;
@ -749,7 +743,7 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
int x = atoi(xp);
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 ( x < 0 )
x = 0;
@ -767,82 +761,83 @@ bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) {
else
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( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() );
delete[] coords;
delete[] str_ptr;
return( true );
return true;
}
bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) {
Debug( 3, "Parsing zone string '%s'", zone_string );
bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) {
Debug(3, "Parsing zone string '%s'", zone_string);
char *str_ptr = new char[strlen(zone_string)+1];
char *str = str_ptr;
strcpy( str, zone_string );
strcpy(str, zone_string);
char *ws = strchr( str, ' ' );
char *ws = strchr(str, ' ');
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 );
Debug( 3, "Got zone %d from zone string", zone_id );
zone_id = strtol(str, 0, 10);
Debug(3, "Got zone %d from zone string", zone_id);
if ( !ws ) {
delete[] str_ptr;
return( true );
return true;
}
*ws = '\0';
str = ws+1;
ws = strchr( str, ' ' );
ws = strchr(str, ' ');
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 );
Debug( 3, "Got colour %06x from zone string", colour );
colour = strtol(str, 0, 16);
Debug(3, "Got colour %06x from zone string", colour);
if ( !ws ) {
delete[] str_ptr;
return( true );
return true;
}
*ws = '\0';
str = ws+1;
bool result = ParsePolygonString( str, polygon );
bool result = ParsePolygonString(str, polygon);
//printf( "Area: %d\n", pg.Area() );
//printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() );
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];
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();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
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());
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock();
return 0;
}
MYSQL_RES *result = mysql_store_result( &dbconn );
db_mutex.unlock();
if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
Error("Can't use query result: %s", mysql_error(&dbconn));
return 0;
}
int n_zones = mysql_num_rows( result );
Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() );
int n_zones = mysql_num_rows(result);
Debug(1, "Got %d zones for monitor %s", n_zones, monitor->Name());
delete[] 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 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 */
AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR);
Debug( 5, "Parsing polygon %s", Coords );
Debug(5, "Parsing polygon %s", Coords);
Polygon 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() );
if ( !ParsePolygonString(Coords, polygon) ) {
Error("Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name());
n_zones -= 1;
continue;
}
if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width()
|| 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;
continue;
}
@ -895,21 +891,17 @@ int Zone::Load( Monitor *monitor, Zone **&zones ) {
}
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 ) {
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 );
}
if ( mysql_errno( &dbconn ) ) {
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
mysql_free_result( result );
return( n_zones );
}
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
mysql_free_result(result);
return n_zones;
} // end int Zone::Load(Monitor *monitor, Zone **&zones)
bool Zone::DumpSettings( char *output, bool /*verbose*/ ) {
bool Zone::DumpSettings(char *output, bool /*verbose*/) {
output[0] = 0;
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 hi_y;
if(max_pixel_threshold)
if ( max_pixel_threshold )
calc_max_pixel_threshold = max_pixel_threshold;
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 hi_x = ranges[y].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 );
const uint8_t *ppoly = ppoly_image->Buffer( lo_x, y );
Debug(7, "Checking line %d from %d -> %d", y, lo_x, hi_x);
uint8_t *pdiff = (uint8_t*)pdiff_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++ ) {
if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) {

View File

@ -94,6 +94,7 @@ protected:
int overload_count;
int extend_alarm_count;
char diag_path[PATH_MAX];
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 );

View File

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

View File

@ -149,7 +149,7 @@ int main(int argc, char *argv[]) {
std::cout << ZM_VERSION << "\n";
exit(0);
default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
// fprintf(stderr, "?? getopt returned character code 0%o ??\n", c);
break;
}
}
@ -162,7 +162,7 @@ int main(int argc, char *argv[]) {
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 ) {
fprintf(stderr, "Only one of device, host/port/path, file or monitor id allowed\n");
Usage();
@ -222,26 +222,31 @@ int main(int argc, char *argv[]) {
}
Info("Starting Capture version %s", ZM_VERSION);
zmSetDefaultHupHandler();
zmSetDefaultTermHandler();
zmSetDefaultDieHandler();
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, SIGHUP);
sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2);
int result = 0;
while( ! zm_terminate ) {
while ( !zm_terminate ) {
result = 0;
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);
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) ) {
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
@ -250,16 +255,19 @@ int main(int argc, char *argv[]) {
sleep(10);
continue;
}
for ( int i = 0; i < n_monitors; i ++ ) {
snprintf(sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id());
for ( int i = 0; i < n_monitors; i++ ) {
snprintf(sql, sizeof(sql),
"REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')",
monitors[i]->Id());
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
}
AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors];
long *capture_delays = new long[n_monitors];
long *alarm_capture_delays = new long[n_monitors];
int *capture_delays = new int[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];
for ( int i = 0; i < n_monitors; i++ ) {
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 ) {
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;
break;
}
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);
monitors[i]->Close();
result = -1;
break;
}
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);
monitors[i]->Close();
result = -1;
break;
}
@ -305,9 +316,9 @@ int main(int argc, char *argv[]) {
gettimeofday(&now, NULL);
// capture_delay is the amount of time we should sleep to achieve the desired framerate.
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);
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;
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();
}
logTerm();
logInit( log_id_string );
logInit(log_id_string);
zm_reload = false;
} // end if zm_reload
} // end while ! zm_terminate and connected
@ -353,16 +364,16 @@ int main(int argc, char *argv[]) {
}
} // end foreach monitor
delete [] analysis_threads;
if ( !zm_terminate )
sleep(10);
} // end while ! zm_terminate outer connection loop
Debug(1,"Updating Monitor status");
for ( int i = 0; i < n_monitors; i++ ) {
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
snprintf(sql, sizeof(sql),
"REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')",
monitors[i]->Id());
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
delete monitors[i];
}

View File

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

View File

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

View File

@ -27,7 +27,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then
echo
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
echo

View File

@ -20,19 +20,6 @@ checksanity () {
fi
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
if [ -z "${OS}" ] || [ -z "${DIST}" ]; then
echo "ERROR: both OS and DIST environment variables must be set"
@ -103,7 +90,7 @@ commonprep () {
fi
# Rpm builds are broken in latest packpack master. Temporarily roll back.
git -C packpack checkout 7cf23ee
#git -C packpack checkout 7cf23ee
# Patch packpack
patch --dry-run --silent -f -p1 < utils/packpack/packpack-rpm.patch
@ -118,27 +105,40 @@ commonprep () {
fi
# fix 32bit rpm builds
patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/setarch.patch
fi
# FIXME: breaks arm rpm builds
#patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
#if [ $? -eq 0 ]; then
# 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
CRUDVER="3.0.10"
CRUDVER="3.1.0-zm"
if [ -e "build/crud-${CRUDVER}.tar.gz" ]; then
echo "Found existing Crud ${CRUDVER} tarball..."
else
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
echo "ERROR: Crud tarball retreival failed..."
exit 1
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 () {
if [ -e "web/api/app/Plugin/Crud/LICENSE.txt" ]; then
echo "Crud plugin already installed..."
@ -148,6 +148,14 @@ movecrud () {
rmdir web/api/app/Plugin/Crud
mv -f crud-${CRUDVER} web/api/app/Plugin/Crud
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
@ -259,10 +267,43 @@ execpackpack () {
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 #
################
# 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
# 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
mkdir web/api/app/Plugin/Crud
# We use zmrepo to build el6 only. All other redhat distros use rpm fusion
if [ "${OS}" == "el" ] && [ "${DIST}" == "6" ]; then
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
if [ -n "$dlurl" ] && [ $? -eq 0 ]; then
echo "Retrieving ${reporpm} repo rpm..."gd
echo "Retrieving ${reporpm} repo rpm..."
curl $dlurl > build/external-repo.noarch.rpm
else
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

@ -302,8 +302,8 @@ function collectData() {
}
}
}
#print_r( $data );
return( $data );
#Logger::Debug(print_r($data, true));
return $data;
}
$data = collectData();
@ -345,8 +345,8 @@ function getFrameImage() {
$eventId = $_REQUEST['id'][0];
$frameId = $_REQUEST['id'][1];
$sql = 'select * from Frames where EventId = ? and FrameId = ?';
if ( !($frame = dbFetchOne( $sql, NULL, array( $eventId, $frameId ) )) ) {
$sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?';
if ( !($frame = dbFetchOne( $sql, NULL, array($eventId, $frameId ) )) ) {
$frame = array();
$frame['EventId'] = $eventId;
$frame['FrameId'] = $frameId;

View File

@ -145,7 +145,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
}
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['rate'] /= RATE_BASE;
$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,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,5 +1,7 @@
<?php
$event_cache = array();
class Event {
private $fields = array(
@ -15,12 +17,12 @@ class Event {
public function __construct( $IdOrRow = null ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) );
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error('Unable to load Event record for Id=' . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
@ -35,6 +37,8 @@ class Event {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
global $event_cache;
$event_cache[$row['Id']] = $this;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
@ -48,10 +52,11 @@ class Event {
if ( $new ) {
$this->{'Storage'} = $new;
}
if ( ! ( array_key_exists( 'Storage', $this ) and $this->{'Storage'} ) ) {
$this->{'Storage'} = isset($this->{'StorageId'}) ?
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
new Storage(NULL);
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
$this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'}));
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) )
$this->{'Storage'} = new Storage(NULL);
}
return $this->{'Storage'};
}
@ -70,12 +75,12 @@ class Event {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$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() {
if ( ! isset( $this->{'Time'} ) ) {
if ( ! isset($this->{'Time'}) ) {
$this->{'Time'} = strtotime($this->{'StartTime'});
}
return $this->{'Time'};
@ -97,7 +102,7 @@ class Event {
$event_path = $this->{'MonitorId'} .'/'.$this->{'Id'};
}
return( $event_path );
return $event_path;
} // end function Relative_Path()
public function Link_Path() {
@ -161,6 +166,8 @@ class Event {
} # end Event->delete
public function getStreamSrc( $args=array(), $querySep='&' ) {
if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) {
$streamSrc = ZM_BASE_PROTOCOL.'://';
$Monitor = $this->Monitor();
@ -174,7 +181,19 @@ class Event {
$args['eid'] = $this->{'Id'};
$args['view'] = 'view_video';
} 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['event'] = $this->{'Id'};
@ -207,8 +226,8 @@ class Event {
$this->{'DiskSpace'} = $new;
}
if ( null === $this->{'DiskSpace'} ) {
$this->{'DiskSpace'} = folder_size( $this->Path() );
dbQuery( 'UPDATE Events SET DiskSpace=? WHERE Id=?', array( $this->{'DiskSpace'}, $this->{'Id'} ) );
$this->{'DiskSpace'} = folder_size($this->Path());
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
}
return $this->{'DiskSpace'};
}
@ -253,7 +272,7 @@ class Event {
// frame is an array representing the db row for a frame.
function getImageSrc($frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false) {
$Storage = new Storage(isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL);
$Storage = $this->Storage();
$Event = $this;
$eventPath = $Event->Path();
@ -370,6 +389,25 @@ class Event {
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 ) {
$filters = array();
$sql = 'SELECT * FROM Events ';

View File

@ -73,18 +73,19 @@ class Frame {
$file = $backTrace[1]['file'];
$line = $backTrace[1]['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 ) {
return array_map( function($id){ return new Frame($id); }, $results );
}
return array();
}
public static function find_one( $parameters = array() ) {
$results = Frame::find( $parameters, 1 );
if ( ! sizeof( $results ) ) {
if ( ! sizeof($results) ) {
return;
}
return $results[0];

View File

@ -20,7 +20,7 @@ class Group {
if ( ! $row ) {
Error('Unable to load Group record for Id=' . $IdOrRow);
}
} elseif ( is_array( $IdOrRow ) ) {
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
@ -36,8 +36,8 @@ class Group {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
}
$group_cache[$row['Id']] = $this;
}
} // end function __construct
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;
if (
( count($parameters) == 1 ) and
@ -109,13 +109,13 @@ class Group {
}
public function delete() {
if ( array_key_exists( 'Id', $this ) ) {
dbQuery( 'DELETE FROM Groups WHERE Id=?', array($this->{'Id'}) );
if ( array_key_exists('Id', $this) ) {
dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}) );
dbQuery( 'DELETE FROM Groups WHERE Id=?', array($this->{'Id'}) );
if ( isset($_COOKIE['zmGroup']) ) {
if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) {
unset( $_COOKIE['zmGroup'] );
setcookie( 'zmGroup', '', time()-3600*24*2 );
unset($_COOKIE['zmGroup']);
setcookie('zmGroup', '', time()-3600*24*2);
}
}
}
@ -123,16 +123,16 @@ class Group {
public function set( $data ) {
foreach ($data as $k => $v) {
if ( is_array( $v ) ) {
if ( is_array($v) ) {
$this->{$k} = $v;
} else if ( is_string( $v ) ) {
} else if ( is_string($v) ) {
$this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) {
} else if ( is_integer($v) ) {
$this->{$k} = $v;
} else if ( is_bool( $v ) ) {
} else if ( is_bool($v) ) {
$this->{$k} = $v;
} else {
Error( "Unknown type $k => $v of var " . gettype( $v ) );
Error("Unknown type $k => $v of var " . gettype($v));
$this->{$k} = $v;
}
}
@ -141,10 +141,10 @@ class Group {
if ( isset($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;
if ( $this->{'ParentId'} != null ) {
$Parent = new Group( $this->{'ParentId'} );
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
$this->{'depth'} += $Parent->depth();
}
}
@ -152,8 +152,8 @@ class Group {
} // end public function depth
public function MonitorIds( ) {
if ( ! array_key_exists( 'MonitorIds', $this ) ) {
$this->{'MonitorIds'} = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}) );
if ( ! array_key_exists('MonitorIds', $this) ) {
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
}
return $this->{'MonitorIds'};
}
@ -198,12 +198,12 @@ class Group {
}
}
function get_options( $Group ) {
function get_options($Group) {
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()]) ) {
foreach ( $children[$Group->Id()] as $child ) {
$options += get_options( $child );
$options += get_options($child);
}
}
return $options;
@ -211,83 +211,34 @@ class Group {
$group_options = array();
foreach ( $Groups as $id=>$Group ) {
if ( ! $Group->ParentId() ) {
$group_options += get_options( $Group );
$group_options += get_options($Group);
}
}
return $group_options;
}
public static function get_group_dropdowns( $selected = null ) {
# 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 ) {
public static function get_group_sql($group_id) {
$groupSql = '';
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 ) ).')';
$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 {
$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;
} # 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;
if ( isset( $_REQUEST['monitor_id'] ) ) {
if ( isset($_REQUEST['monitor_id']) ) {
$monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId'];
@ -300,14 +251,14 @@ $group_options[$Group->Id()] = str_repeat( '&nbsp;', $depth ) . $Group->Name();
}
$monitors_dropdown = array(''=>'All');
foreach ( dbFetchAll( $sql ) as $monitor ) {
if ( !visibleMonitor( $monitor['Id'] ) ) {
foreach ( dbFetchAll($sql) as $monitor ) {
if ( !visibleMonitor($monitor['Id']) ) {
continue;
}
$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;
}
@ -329,6 +280,4 @@ public function Parents() {
}
} # end class Group
?>

View File

@ -27,6 +27,7 @@ private $defaults = array(
private $status_fields = array(
'AnalysisFPS' => null,
'CaptureFPS' => null,
'CaptureBandwidth' => null,
);
private $control_fields = array(
'Name' => '',
@ -171,6 +172,7 @@ private $control_fields = array(
}
} # end if isset($IdOrRow)
} // end function __construct
public function Server() {
return new Server( $this->{'ServerId'} );
}
@ -255,10 +257,13 @@ private $control_fields = array(
return $this->{'Height'};
}
public function set( $data ) {
public function set($data) {
foreach ($data as $k => $v) {
if ( method_exists($this, $k) ) {
$this->{$k}($v);
} else {
if ( is_array( $v ) ) {
# perhaps should turn into a comma-separated string
# perhaps should turn into a comma-separated string
$this->{$k} = implode(',',$v);
} else if ( is_string( $v ) ) {
$this->{$k} = trim( $v );
@ -270,7 +275,8 @@ private $control_fields = array(
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 ) {
$filters = array();
@ -306,7 +312,7 @@ private $control_fields = array(
return $filters;
}
public function save( $new_values = null ) {
public function save($new_values = null) {
if ( $new_values ) {
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=?';
$values = array_map( function($field){return $this->{$field};}, $fields );
$sql = 'UPDATE Monitors SET '.implode(', ', array_map(function($field) {return $field.'=?';}, $fields )) . ' WHERE Id=?';
$values = array_map(function($field){return $this->{$field};}, $fields);
$values[] = $this->{'Id'};
dbQuery( $sql, $values );
dbQuery($sql, $values);
} // end function save
function zmcControl( $mode=false ) {
@ -401,10 +407,20 @@ private $control_fields = array(
}
} // 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('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 {
$this->{'GroupIds'} = array();
}

View File

@ -13,7 +13,7 @@ class Storage {
if ( ! $row ) {
Error("Unable to load Storage record for Id=" . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
} else if ( is_array($IdOrRow) ) {
$row = $IdOrRow;
}
}
@ -21,7 +21,7 @@ class Storage {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
$storage_cache[$IdOrRow] = $this;
$storage_cache[$row['Id']] = $this;
} else {
$this->{'Name'} = '';
$this->{'Path'} = '';
@ -97,7 +97,7 @@ class Storage {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
@ -105,7 +105,7 @@ class Storage {
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
$sql .= implode(' AND ', $fields);
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
@ -140,16 +140,17 @@ class Storage {
return $usage;
}
public function disk_total_space() {
if ( ! array_key_exists( 'disk_total_space', $this ) ) {
$this->{'disk_total_space'} = disk_total_space( $this->Path() );
if ( ! array_key_exists('disk_total_space', $this) ) {
$this->{'disk_total_space'} = disk_total_space($this->Path());
}
return $this->{'disk_total_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.
if ( (! array_key_exists( 'DiskSpace', $this )) or (!$this->{'DiskSpace'}) ) {
$used = 0;
if ( (! array_key_exists('disk_used_space', $this)) or (!$this->{'disk_used_space'}) ) {
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()) );
if ( 0 ) {
foreach ( Event::find_all( array( 'StorageId'=>$this->Id(), 'DiskSpace'=>null ) ) as $Event ) {
@ -157,14 +158,35 @@ if ( 0 ) {
$used += $Event->DiskSpace();
}
}
=======
$this->{'disk_used_space'} = $this->disk_event_space();
>>>>>>> storageareas
} else {
$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;
}
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
if ( canView( 'Events' ) ) {
if ( canView('Events') ) {
if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( $action == 'addterm' ) {
@ -155,7 +155,7 @@ if ( canView( 'Events' ) ) {
} else if ( canEdit( 'Events' ) ) {
if ( $action == 'delete' ) {
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' ) ) {
# or ( $action == 'submit' ) ) {
@ -189,9 +189,9 @@ if ( canView( 'Events' ) ) {
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
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 {
dbQuery( 'INSERT INTO Filters SET' . $sql );
dbQuery('INSERT INTO Filters SET' . $sql);
$_REQUEST['Id'] = dbInsertId();
}
if ( $action == 'execute' ) {
@ -207,7 +207,7 @@ if ( canView( 'Events' ) ) {
// Event scope actions, edit permissions required
if ( canEdit('Events') ) {
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' ) {
if ( !empty($_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;
}
$Monitor = new Monitor( $mid );
if ( $Monitor->Type() != 'WebSite' ) {
$Monitor->zmaControl('stop');
$Monitor->zmcControl('stop');
}
$Monitor->save( $_REQUEST['newMonitor'] );
if ($Monitor->Function() != 'None' ) {
if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('start');
if ( $Monitor->Enabled() ) {
$Monitor->zmaControl('start');
@ -330,7 +332,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled;
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, $restart?'restart':'' );
@ -371,7 +373,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
}
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'restart' );
@ -399,7 +401,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
}
}
if($changes>0) {
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
zmaControl( $mid, 'restart' );
}
$refreshParent = true;
@ -424,7 +426,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$deletedZid = 1;
}
if ( $deletedZid ) {
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $zone['Type'] == 'Privacy' ) {
zmaControl( $mid, 'stop' );
zmcControl( $mid, 'restart' );
@ -458,7 +460,7 @@ if ( canEdit( 'Monitors' ) ) {
$x10Monitor = array();
}
}
$Monitor = new Monitor( $monitor );
$Monitor = new Monitor($monitor);
// Define a field type for anything that's not simple text equivalent
$types = array(
@ -475,7 +477,7 @@ if ( canEdit( 'Monitors' ) ) {
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
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'] );
if ( ( ! $_REQUEST['newMonitor'] ) and defined('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']);
}
$columns = getTableColumns( 'Monitors' );
$changes = getFormChanges( $monitor, $_REQUEST['newMonitor'], $types, $columns );
$columns = getTableColumns('Monitors');
$changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
if ( count( $changes ) ) {
if ( $mid ) {
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
if ( $monitor['Type'] != 'WebSite' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'stop' );
}
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
// Groups will be added below
if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
@ -564,7 +568,16 @@ if ( canEdit( 'Monitors' ) ) {
Error("Users with Monitors restrictions cannot create new monitors.");
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() )
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
$restart = true;
} # end if count(changes)
if ( ZM_OPT_X10 ) {
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
@ -599,8 +610,10 @@ if ( canEdit( 'Monitors' ) ) {
$new_monitor = new Monitor($mid);
//fixDevices();
if ( $monitor['Type'] != 'WebSite' ) {
$new_monitor->zmcControl('start');
$new_monitor->zmaControl('start');
}
if ( $new_monitor->Controllable() ) {
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( 'ZM_VERSION', '@VERSION@' ); // Version
define( 'ZM_DIR_TEMP', '@ZM_TMPDIR@' );
define( 'ZM_DIR_CACHE', '@ZM_CACHEDIR@' );
global $configvals;
$configFile = ZM_CONFIG;

View File

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

View File

@ -280,6 +280,27 @@ function getImageStill( $id, $src, $width, $height, $title='' ) {
return '<img id="'.$id.'" src="'.$src.'" alt="'.$title.'"'.(validInt($width)?' width="'.$width.'"':'').(validInt($height)?' height="'.$height.'"':'').'/>';
}
function getWebSiteUrl( $id, $src, $width, $height, $title='' ) {
# Prevent unsightly warnings when php cannot verify the ssl certificate
stream_context_set_default( [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
# The End User can turn off the following warning under Options -> Web
if ( ZM_WEB_XFRAME_WARN ) {
$header = get_headers($src, 1);
# If the target website has set X-Frame-Options, check it for "sameorigin" and warn the end user
if (array_key_exists('X-Frame-Options', $header)) {
$header = $header['X-Frame-Options'];
if ( stripos($header, 'sameorigin') === 0 )
Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site.");
}
}
return '<object id="'.$id.'" data="'.$src.'" alt="'.$title.'" width="'.$width.'" height="'.$height.'"></object>';
}
function outputControlStill( $src, $width, $height, $monitor, $scale, $target ) {
?>
<form name="ctrlForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>" target="<?php echo $target ?>">
@ -486,7 +507,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
$types = array();
foreach( $newValues as $key=>$value ) {
if ( $columns && !$columns[$key] )
if ( $columns && !isset($columns[$key]) )
continue;
if ( !isset($types[$key]) )
@ -495,11 +516,11 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
switch( $types[$key] ) {
case 'set' :
{
if ( is_array( $newValues[$key] ) ) {
if ( join(',',$newValues[$key]) != $values[$key] ) {
if ( is_array($newValues[$key]) ) {
if ( (!isset($values[$key])) or ( join(',',$newValues[$key]) != $values[$key] ) ) {
$changes[$key] = "`$key` = ".dbEscape(join(',',$newValues[$key]));
}
} elseif ( $values[$key] ) {
} else if ( (!isset($values[$key])) or $values[$key] ) {
$changes[$key] = "`$key` = ''";
}
break;
@ -548,7 +569,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
}
case 'raw' :
{
if ( $values[$key] != $value ) {
if ( (!isset($values[$key])) or ($values[$key] != $value) ) {
$changes[$key] = $key . ' = '.dbEscape($value);
}
break;
@ -716,18 +737,14 @@ Logger::Debug("daemonControl $string");
exec( $string );
}
function zmcControl( $monitor, $mode=false ) {
function zmcControl($monitor, $mode=false) {
$Monitor = new Monitor( $monitor );
return $Monitor->zmcControl($mode);
}
function zmaControl( $monitor, $mode=false ) {
if ( !is_array( $monitor ) ) {
$monitor =
$monitor = dbFetchOne( 'select C.*, M.* from Monitors as M left join Controls as C on (M.ControlId = C.Id ) where M.Id=?', NULL, array($monitor) );
}
$Monitor = new Monitor( $monitor );
$Monitor->zmaControl($mode);
function zmaControl($monitor, $mode=false) {
$Monitor = new Monitor($monitor);
return $Monitor->zmaControl($mode);
}
function initDaemonStatus() {
@ -979,7 +996,7 @@ function parseSort( $saveToSession=false, $querySep='&amp;' ) {
}
}
function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
function parseFilter(&$filter, $saveToSession=false, $querySep='&amp;') {
$filter['query'] = '';
$filter['sql'] = '';
$filter['fields'] = '';
@ -997,7 +1014,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
}
if ( isset($terms[$i]['obr']) ) {
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($terms[$i]['obr']);
$filter['sql'] .= ' '.str_repeat( '(', $terms[$i]['obr'] ).' ';
$filter['sql'] .= ' '.str_repeat('(', $terms[$i]['obr']).' ';
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][obr]\" value=\"".htmlspecialchars($terms[$i]['obr'])."\"/>\n";
}
if ( isset($terms[$i]['attr']) ) {
@ -1005,11 +1022,18 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][attr]\" value=\"".htmlspecialchars($terms[$i]['attr'])."\"/>\n";
switch ( $terms[$i]['attr'] ) {
case 'MonitorName':
$filter['sql'] .= 'M.'.preg_replace( '/^Monitor/', '', $terms[$i]['attr'] );
$filter['sql'] .= 'M.'.preg_replace('/^Monitor/', '', $terms[$i]['attr']);
break;
case 'ServerId':
case 'MonitorServerId':
$filter['sql'] .= 'M.ServerId';
break;
case 'StorageServerId':
$filter['sql'] .= 'S.ServerId';
break;
case 'FilterServerId':
$filter['sql'] .= ZM_SERVER_ID;
break;
# Unspecified start or end, so assume start, this is to support legacy filters
case 'DateTime':
$filter['sql'] .= 'E.StartTime';
@ -1104,6 +1128,9 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
case 'Notes':
$value = dbEscape($value);
break;
case 'MonitorServerId':
case 'FilterServerId':
case 'StorageServerId':
case 'ServerId':
if ( $value == 'ZM_SERVER_ID' ) {
$value = ZM_SERVER_ID;
@ -2035,9 +2062,9 @@ function cache_bust( $file ) {
$parts = pathinfo($file);
global $css;
$dirname = preg_replace( '/\//', '_', $parts['dirname'] );
$cacheFile = 'cache/'.$dirname.'_'.$parts['filename'].'-'.$css.'-'.filemtime($file).'.'.$parts['extension'];
if ( file_exists( ZM_PATH_WEB.'/'.$cacheFile ) or symlink( ZM_PATH_WEB.'/'.$file, ZM_PATH_WEB.'/'.$cacheFile ) ) {
return $cacheFile;
$cacheFile = $dirname.'_'.$parts['filename'].'-'.$css.'-'.filemtime($file).'.'.$parts['extension'];
if ( file_exists( ZM_DIR_CACHE.'/'.$cacheFile ) or symlink( ZM_PATH_WEB.'/'.$file, ZM_DIR_CACHE.'/'.$cacheFile ) ) {
return 'cache/'.$cacheFile;
} else {
Warning("Failed linking $file to $cacheFile");
}
@ -2132,11 +2159,17 @@ function getStreamHTML( $monitor, $options = array() ) {
$options['buffer'] = $monitor->StreamReplayBuffer();
//Warning("width: " . $options['width'] . ' height: ' . $options['height']. ' scale: ' . $options['scale'] );
if ( $monitor->Type() == "WebSite" ) {
return getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(),
( isset($options['width']) ? $options['width'] : NULL ),
( isset($options['height']) ? $options['height'] : NULL ),
$monitor->Name()
);
//FIXME, the width and height of the image need to be scaled.
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
} else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
$streamSrc = $monitor->getStreamSrc( array(
'mode'=>'mpeg',
'scale'=>$options['scale'],
'scale'=>(isset($options['scale'])?$options['scale']:100),
'bitrate'=>ZM_WEB_VIDEO_BITRATE,
'maxfps'=>ZM_WEB_VIDEO_MAXFPS,
'format' => ZM_MPEG_LIVE_FORMAT

View File

@ -145,6 +145,9 @@ if ( ZM_OPT_USE_AUTH ) {
} else {
$user = $defaultUser;
}
# Only one request can open the session file at a time, so let's close the session here to improve concurrency.
# Any file/page that sets session variables must re-open it.
session_write_close();
require_once( 'includes/lang.php' );
require_once( 'includes/functions.php' );
@ -158,7 +161,7 @@ CORSHeaders();
// Check for valid content dirs
if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) ) {
Error( "Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user");
Warning("Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user");
}
# Globals
@ -187,9 +190,6 @@ if ( ZM_OPT_USE_AUTH ) {
generateAuthHash( ZM_AUTH_HASH_IPS );
}
}
# Only one request can open the session file at a time, so let's close the session here to improve concurrency.
# Any file/page that sets session variables must re-open it.
session_write_close();
if ( isset($_REQUEST['action']) ) {
$action = detaintPath($_REQUEST['action']);

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