Merge branch 'storageareas' into zma_to_thread
This commit is contained in:
commit
f6139d33ed
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
--
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||
@ZM_LOGDIR@/*log
|
||||
{
|
||||
weekly
|
||||
notifempty
|
||||
missingok
|
||||
create 660 @WEB_USER@ @WEB_GROUP@
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
-----------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -5,4 +5,3 @@ Name=ZoneMinder
|
|||
Comment=
|
||||
Icon=@PKGDATADIR@/icons/16x16/icon.xpm
|
||||
URL=http://localhost/zm/\r
|
||||
Categories=GNOME;AudioVideo;Video;Recorder;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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}'";
|
||||
|
|
|
@ -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'=> {
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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} ) {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -73,6 +73,7 @@ public:
|
|||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
int Close() { return 0; };
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -69,7 +69,7 @@ class FfmpegCamera : public Camera {
|
|||
AVPacket packet;
|
||||
|
||||
int OpenFfmpeg();
|
||||
int CloseFfmpeg();
|
||||
int Close();
|
||||
bool mCanCapture;
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
int PreCapture();
|
||||
int Capture( ZMPacket &p );
|
||||
int PostCapture();
|
||||
int Close() { return 0; };
|
||||
};
|
||||
|
||||
#endif // ZM_FILE_CAMERA_H
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
int PreCapture();
|
||||
int Capture( ZMPacket &p );
|
||||
int PostCapture();
|
||||
int Close() { return 0; };
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBVLC
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -85,6 +85,7 @@ public:
|
|||
int PreCapture();
|
||||
int Capture( ZMPacket &p );
|
||||
int PostCapture();
|
||||
int Close() { return 0; };
|
||||
};
|
||||
|
||||
#endif // ZM_REMOTE_CAMERA_RTSP_H
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
346
src/zm_zone.cpp
346
src/zm_zone.cpp
|
@ -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) ) {
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
47
src/zmc.cpp
47
src/zmc.cpp
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
214
src/zmu.cpp
214
src/zmu.cpp
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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..."
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ app/webroot/ [L]
|
||||
RewriteRule (.*) app/webroot/$1 [L]
|
||||
</IfModule>
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ app/webroot/ [L]
|
||||
RewriteRule (.*) app/webroot/$1 [L]
|
||||
RewriteBase /zm/api
|
||||
</IfModule>
|
|
@ -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
|
|
@ -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>
|
|
@ -1,5 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ webroot/ [L]
|
||||
RewriteRule (.*) webroot/$1 [L]
|
||||
</IfModule>
|
|
@ -1,6 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
|
@ -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 ';
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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(' ', $Group->depth() ) . $Group->Name() );
|
||||
$options = array($Group->Id() => str_repeat(' ', $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( ' ', $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( ' ', $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
|
||||
|
||||
|
||||
?>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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'};
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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' );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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='&' ) {
|
|||
}
|
||||
}
|
||||
|
||||
function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
||||
function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||
$filter['query'] = '';
|
||||
$filter['sql'] = '';
|
||||
$filter['fields'] = '';
|
||||
|
@ -997,7 +1014,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
|||
}
|
||||
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='&' ) {
|
|||
$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='&' ) {
|
|||
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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue