Merge branch 'zma_to_thread' of github.com:connortechnology/ZoneMinder into zma_to_thread
This commit is contained in:
commit
cf5eab4dbc
|
@ -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
|
||||
|
|
16
.travis.yml
16
.travis.yml
|
@ -23,17 +23,27 @@ addons:
|
|||
- curl
|
||||
- sshfs
|
||||
- sed
|
||||
- binfmt-support
|
||||
- qemu
|
||||
- qemu-user-static
|
||||
- dnsutils
|
||||
- traceroute
|
||||
install:
|
||||
- update-binfmts --enable qemu-arm
|
||||
|
||||
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
|
||||
- OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack
|
||||
- OS=ubuntu DIST=trusty
|
||||
- OS=ubuntu DIST=xenial
|
||||
- OS=ubuntu DIST=trusty ARCH=i386
|
||||
- OS=ubuntu DIST=xenial ARCH=i386
|
||||
- OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
services:
|
||||
|
|
|
@ -58,6 +58,8 @@ if(NOT HOST_OS)
|
|||
"ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
|
||||
endif(NOT HOST_OS)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
# Default CLFAGS and CXXFLAGS:
|
||||
set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
|
||||
|
@ -141,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
|
||||
|
@ -205,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")
|
||||
|
||||
|
@ -484,24 +488,24 @@ if(MP4V2_LIBRARIES)
|
|||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
|
||||
|
||||
# mp4v2.h
|
||||
find_path(MP4V2_INCLUDE_DIR mp4v2.h)
|
||||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
check_include_file("mp4v2.h" HAVE_MP4V2_H)
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4v2.h" HAVE_MP4V2_H)
|
||||
|
||||
# mp4.h
|
||||
find_path(MP4V2_INCLUDE_DIR mp4.h)
|
||||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
check_include_file("mp4.h" HAVE_MP4_H)
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4.h" HAVE_MP4_H)
|
||||
|
||||
mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR)
|
||||
set(optlibsfound "${optlibsfound} mp4v2")
|
||||
|
|
|
@ -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,5 +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")
|
||||
|
||||
|
|
|
@ -116,39 +116,26 @@ FOR EACH ROW
|
|||
END;
|
||||
//
|
||||
|
||||
drop procedure if exists update_storage_stats;
|
||||
create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT)
|
||||
|
||||
sql security invoker
|
||||
|
||||
deterministic
|
||||
|
||||
begin
|
||||
|
||||
update Storage set DiskSpace = COALESCE(DiskSpace,0) + COALESCE(space,0) where Id = StorageId;
|
||||
|
||||
end;
|
||||
|
||||
//
|
||||
drop procedure if exists update_storage_stats//
|
||||
|
||||
drop trigger if exists event_update_trigger//
|
||||
|
||||
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( NEW.StorageId = OLD.StorageID ) THEN
|
||||
IF ( diff ) THEN
|
||||
call update_storage_stats(OLD.StorageId, diff);
|
||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
ELSE
|
||||
IF ( NEW.DiskSpace ) THEN
|
||||
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
|
||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
|
||||
END IF;
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
|
@ -216,7 +203,7 @@ CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
|
|||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Day WHERE EventId=OLD.Id;
|
||||
|
|
|
@ -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',
|
||||
|
@ -182,7 +182,7 @@ CREATE TABLE `Devices` (
|
|||
|
||||
DROP TABLE IF EXISTS `Events`;
|
||||
CREATE TABLE `Events` (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`Id` bigint unsigned NOT NULL auto_increment,
|
||||
`MonitorId` int(10) unsigned NOT NULL default '0',
|
||||
`StorageId` smallint(5) unsigned default 0,
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
|
@ -210,6 +210,7 @@ CREATE TABLE `Events` (
|
|||
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
|
||||
`Locked` BOOLEAN NOT NULL DEFAULT False,
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `Events_MonitorId_idx` (`MonitorId`),
|
||||
KEY `Events_StorageId_idx` (`StorageId`),
|
||||
|
@ -219,7 +220,7 @@ CREATE TABLE `Events` (
|
|||
|
||||
DROP TABLE IF EXISTS `Events_Hour`;
|
||||
CREATE TABLE `Events_Hour` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
`EventId` BIGINT unsigned NOT NULL,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
`StartTime` datetime default NULL,
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
|
@ -228,40 +229,9 @@ CREATE TABLE `Events_Hour` (
|
|||
KEY `Events_Hour_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
|
||||
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
HourEvents = COALESCE(HourEvents,1)-1,
|
||||
HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
|
||||
|
||||
CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Day`;
|
||||
CREATE TABLE `Events_Day` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
`EventId` BIGINT unsigned NOT NULL,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
`StartTime` datetime default NULL,
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
|
@ -270,40 +240,9 @@ CREATE TABLE `Events_Day` (
|
|||
KEY `Events_Day_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
|
||||
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
DayEvents = COALESCE(DayEvents,1)-1,
|
||||
DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Day_update_trigger;
|
||||
CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DELIMITER ;
|
||||
DROP TABLE IF EXISTS `Events_Week`;
|
||||
CREATE TABLE `Events_Week` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
`EventId` BIGINT unsigned NOT NULL,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
`StartTime` datetime default NULL,
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
|
@ -312,40 +251,9 @@ CREATE TABLE `Events_Week` (
|
|||
KEY `Events_Week_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
|
||||
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
WeekEvents = COALESCE(WeekEvents,1)-1,
|
||||
WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Week_update_trigger;
|
||||
CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Month`;
|
||||
CREATE TABLE `Events_Month` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
`EventId` BIGINT unsigned NOT NULL,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
`StartTime` datetime default NULL,
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
|
@ -354,177 +262,16 @@ CREATE TABLE `Events_Month` (
|
|||
KEY `Events_Month_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
|
||||
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
MonthEvents = COALESCE(MonthEvents,1)-1,
|
||||
MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Month_update_trigger;
|
||||
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Archived`;
|
||||
CREATE TABLE `Events_Archived` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
`EventId` BIGINT unsigned NOT NULL,
|
||||
`MonitorId` int(10) unsigned NOT NULL,
|
||||
`DiskSpace` bigint unsigned default NULL,
|
||||
PRIMARY KEY (`EventId`),
|
||||
KEY `Events_Archived_MonitorId_idx` (`MonitorId`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
|
||||
drop procedure if exists update_storage_stats;
|
||||
|
||||
delimiter //
|
||||
|
||||
create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT)
|
||||
|
||||
sql security invoker
|
||||
|
||||
deterministic
|
||||
|
||||
begin
|
||||
|
||||
update Storage set DiskSpace = COALESCE(DiskSpace,0) + COALESCE(space,0) where Id = StorageId;
|
||||
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
drop trigger if exists event_update_trigger//
|
||||
|
||||
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( NEW.StorageId = OLD.StorageID ) THEN
|
||||
IF ( diff ) THEN
|
||||
call update_storage_stats(OLD.StorageId, diff);
|
||||
END IF;
|
||||
ELSE
|
||||
IF ( NEW.DiskSpace ) THEN
|
||||
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
|
||||
END IF;
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
|
||||
IF ( NEW.Archived != OLD.Archived ) THEN
|
||||
IF ( NEW.Archived ) THEN
|
||||
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
|
||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
|
||||
ELSEIF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
ELSE IF ( NEW.Archived AND diff ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
END IF;
|
||||
|
||||
IF ( diff ) THEN
|
||||
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
delimiter ;
|
||||
|
||||
DROP TRIGGER IF EXISTS event_insert_trigger;
|
||||
|
||||
delimiter //
|
||||
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
|
||||
* The DiskSpace will get update in the Event Update Trigger
|
||||
*/
|
||||
CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
|
||||
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
UPDATE Monitors SET
|
||||
HourEvents = COALESCE(DayEvents,0)+1,
|
||||
DayEvents = COALESCE(DayEvents,0)+1,
|
||||
WeekEvents = COALESCE(DayEvents,0)+1,
|
||||
MonthEvents = COALESCE(DayEvents,0)+1,
|
||||
TotalEvents = COALESCE(TotalEvents,0)+1
|
||||
WHERE Id=NEW.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS event_delete_trigger//
|
||||
|
||||
CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
END IF;
|
||||
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Day WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Week WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Month WHERE EventId=OLD.Id;
|
||||
IF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
|
||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
|
||||
TotalEvents = COALESCE(TotalEvents,1) - 1,
|
||||
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET
|
||||
TotalEvents = COALESCE(TotalEvents,1)-1,
|
||||
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
delimiter ;
|
||||
--
|
||||
-- Table structure for table `Filters`
|
||||
--
|
||||
|
@ -557,8 +304,8 @@ CREATE TABLE `Filters` (
|
|||
|
||||
DROP TABLE IF EXISTS `Frames`;
|
||||
CREATE TABLE `Frames` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`EventId` int(10) unsigned NOT NULL default '0',
|
||||
`Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`EventId` BIGINT UNSIGNED NOT NULL default '0',
|
||||
`FrameId` int(10) unsigned NOT NULL default '0',
|
||||
`Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal',
|
||||
`TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
|
||||
|
@ -710,7 +457,8 @@ CREATE TABLE `Monitors` (
|
|||
`Deinterlacing` int(10) unsigned NOT NULL default '0',
|
||||
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
|
||||
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
|
||||
`OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'),
|
||||
`OutputCodec` int(10) unsigned NOT NULL default 0,
|
||||
`Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2'),
|
||||
`OutputContainer` enum('auto','mp4','mkv'),
|
||||
`EncoderParameters` TEXT,
|
||||
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
|
||||
|
@ -752,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',
|
||||
|
@ -780,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;
|
||||
--
|
||||
|
@ -949,40 +699,24 @@ CREATE TABLE `Zones` (
|
|||
KEY `MonitorId` (`MonitorId`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
DELIMITER //
|
||||
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
|
||||
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
|
||||
END
|
||||
//
|
||||
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
|
||||
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
|
||||
END
|
||||
//
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Storage`;
|
||||
CREATE TABLE `Storage` (
|
||||
`Id` smallint(5) unsigned NOT NULL auto_increment,
|
||||
`Path` varchar(64) NOT NULL default '',
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`Type` enum('local','s3fs') NOT NULL default 'local',
|
||||
`Url` varchar(255) default NULL,
|
||||
`DiskSpace` bigint default NULL,
|
||||
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
|
||||
`ServerId` int(10) unsigned,
|
||||
`DoDelete` BOOLEAN NOT NULL DEFAULT true,
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
--
|
||||
-- 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 */;
|
||||
|
@ -1040,6 +774,10 @@ INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0,
|
|||
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
|
||||
--
|
||||
-- Add some monitor preset values
|
||||
|
@ -1138,7 +876,7 @@ CREATE TABLE Maps (
|
|||
PRIMARY KEY (`Id`)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS MontageLayout;
|
||||
DROP TABLE IF EXISTS MontageLayouts;
|
||||
|
||||
CREATE TABLE MontageLayouts (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
|
@ -1155,11 +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.
|
||||
<<<<<<< HEAD
|
||||
source triggers.sql
|
||||
=======
|
||||
source @ZM_PATH_DATA@/db/triggers.sql
|
||||
>>>>>>> storageareas
|
||||
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;
|
|
@ -176,8 +176,10 @@ BEGIN
|
|||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
ELSEIF ( NEW.Archived AND diff ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
ELSE
|
||||
IF ( NEW.Archived AND diff ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF ( diff ) THEN
|
||||
|
@ -185,7 +187,6 @@ BEGIN
|
|||
END IF;
|
||||
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
delimiter ;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Storage'
|
||||
AND column_name = 'Url'
|
||||
) > 0,
|
||||
"SELECT 'Column Url already exists in Storage'",
|
||||
"ALTER TABLE `Storage` ADD `Url` VARCHAR(255) default NULL AFTER `Type`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
||||
|
||||
ALTER TABLE `Monitors` MODIFY `OutputCodec` int(10) UNSIGNED NOT NULL default 0;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'Encoder'
|
||||
) > 0,
|
||||
"SELECT 'Column Encoder already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2') AFTER `OutputCodec`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Storage'
|
||||
AND column_name = 'DoDelete'
|
||||
) > 0,
|
||||
"SELECT 'Column DoDelete already exists in Storage'",
|
||||
"ALTER TABLE `Storage` ADD `DoDelete` BOOLEAN NOT NULL default true AFTER `ServerId`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Events'
|
||||
AND column_name = 'Locked'
|
||||
) > 0,
|
||||
"SELECT 'Column Locked already exists in Events'",
|
||||
"ALTER TABLE `Events` ADD `Locked` BOOLEAN NOT NULL default false AFTER `Scheme`"
|
||||
));
|
||||
|
||||
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;
|
|
@ -5,6 +5,9 @@ set -e
|
|||
if [ "$1" = "configure" ]; then
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
for i in /etc/zm/conf.d/*.conf; do
|
||||
. $i
|
||||
done;
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group.
|
||||
chown www-data:root /var/log/zm
|
||||
|
|
|
@ -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(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")
|
||||
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)
|
||||
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(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
||||
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
||||
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
|
||||
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
|
||||
else(ZM_WEB_USER STREQUAL "nginx")
|
||||
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
||||
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
||||
endif(ZM_WEB_USER STREQUAL "nginx")
|
||||
endif(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
||||
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,37 +1,19 @@
|
|||
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
|
||||
============
|
||||
|
||||
1. Unless you are already using MariaDB server, you need to ensure that the
|
||||
server is configured to start during boot and properly secured by running:
|
||||
|
||||
sudo dnf install mariadb-server
|
||||
sudo systemctl enable mariadb
|
||||
sudo systemctl start mariadb.service
|
||||
mysql_secure_installation
|
||||
sudo dnf install mariadb-server
|
||||
sudo systemctl enable mariadb
|
||||
sudo systemctl start mariadb.service
|
||||
mysql_secure_installation
|
||||
|
||||
2. Assuming the database is local and using the password for the root account
|
||||
set during the previous step, you will need to create the ZoneMinder
|
||||
|
@ -50,13 +32,13 @@ New installs
|
|||
/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}
|
||||
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
|
||||
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
|
||||
|
@ -82,34 +64,62 @@ 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 dnf install mod_ssl
|
||||
This package uses the HTTPS protocol by default to access the web portal,
|
||||
using rhe default self signed certificate on your system. Requests using
|
||||
HTTP will auto-redirect to HTTPS.
|
||||
|
||||
If this does not meet your needs, then read README.https to
|
||||
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:
|
||||
|
||||
sudo systemctl enable httpd
|
||||
sudo systemctl start httpd
|
||||
sudo systemctl enable httpd
|
||||
sudo systemctl start httpd
|
||||
|
||||
8. Now start zoneminder:
|
||||
|
||||
sudo systemctl enable zoneminder
|
||||
sudo systemctl start zoneminder
|
||||
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
|
||||
============
|
||||
|
@ -28,10 +10,10 @@ New installs
|
|||
1. Unless you are already using MariaDB server, you need to ensure that the
|
||||
server is configured to start during boot and properly secured by running:
|
||||
|
||||
sudo yum install mariadb-server
|
||||
sudo systemctl enable mariadb
|
||||
sudo systemctl start mariadb.service
|
||||
mysql_secure_installation
|
||||
sudo yum install mariadb-server
|
||||
sudo systemctl enable mariadb
|
||||
sudo systemctl start mariadb.service
|
||||
mysql_secure_installation
|
||||
|
||||
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
|
||||
|
@ -50,13 +32,13 @@ New installs
|
|||
/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}
|
||||
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
|
||||
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
|
||||
|
@ -82,25 +64,62 @@ 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:
|
||||
|
||||
sudo systemctl enable httpd
|
||||
sudo systemctl start httpd
|
||||
sudo systemctl enable httpd
|
||||
sudo systemctl start httpd
|
||||
|
||||
8. Now start zoneminder:
|
||||
|
||||
sudo systemctl enable zoneminder
|
||||
sudo systemctl start zoneminder
|
||||
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,11 @@ BuildRequires: vlc-devel
|
|||
BuildRequires: libcurl-devel
|
||||
BuildRequires: libv4l-devel
|
||||
BuildRequires: ffmpeg-devel
|
||||
BuildRequires: desktop-file-utils
|
||||
|
||||
# Required for mp4 container support
|
||||
BuildRequires: libmp4v2-devel
|
||||
BuildRequires: x264-devel
|
||||
|
||||
%{?with_nginx:Requires: nginx}
|
||||
%{?with_nginx:Requires: fcgiwrap}
|
||||
|
@ -112,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
|
||||
|
@ -143,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
|
||||
|
@ -163,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 || :
|
||||
|
||||
|
@ -174,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
|
||||
|
@ -245,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
|
||||
|
@ -282,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
|
||||
|
@ -300,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
|
||||
|
@ -337,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*
|
||||
|
@ -347,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
|
||||
|
@ -354,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,6 +54,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,liburi-encode-perl
|
||||
,libwww-perl
|
||||
,libdata-uuid-perl
|
||||
,libnumber-bytes-human-perl
|
||||
,libfile-slurp-perl
|
||||
,mysql-client | virtual-mysql-client
|
||||
,perl-modules
|
||||
,php5-mysql, php5-gd, php5-apcu, php-apc
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,10 @@ set -e
|
|||
if [ "$1" = "configure" ]; then
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
for i in /etc/zm/conf.d/*.conf; do
|
||||
. $i
|
||||
done;
|
||||
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
chown www-data:root /var/log/zm
|
||||
|
|
|
@ -23,8 +23,7 @@ configuration file:
|
|||
Upgrading database
|
||||
------------------
|
||||
|
||||
Prior to 1.28.1 database upgrade was performed automatically.
|
||||
"zoneminder" service will refuse to start with outdated database.
|
||||
The database is updated automatically on installation. You should not need to take this step.
|
||||
|
||||
Assuming that database is on "localhost" then the following command can be
|
||||
used to upgrade "zm" database:
|
||||
|
@ -45,17 +44,11 @@ The following command prints the current version of zoneminder database:
|
|||
Enabling service
|
||||
----------------
|
||||
|
||||
By default Zoneminder service is not starting automatically and need to be
|
||||
manually activated once database is configured:
|
||||
|
||||
On systemd:
|
||||
By default Zoneminder service is not automatically started and needs to be
|
||||
manually enabled once database is configured:
|
||||
|
||||
sudo systemctl enable zoneminder.service
|
||||
|
||||
On SysV:
|
||||
|
||||
sudo update-rc.d zoneminder enable
|
||||
|
||||
|
||||
Web server set-up
|
||||
-----------------
|
||||
|
@ -82,10 +75,10 @@ Common configuration steps for Apache2:
|
|||
|
||||
## nginx / fcgiwrap
|
||||
|
||||
Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package
|
||||
Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package
|
||||
for binary "cgi-bin" applications:
|
||||
|
||||
sudo apt-get install php5-fpm fcgiwrap
|
||||
sudo apt-get install php-fpm fcgiwrap
|
||||
|
||||
To enable a URL alias that makes Zoneminder available from
|
||||
|
||||
|
@ -119,32 +112,9 @@ site configuration.
|
|||
Changing the location for images and events
|
||||
-------------------------------------------
|
||||
|
||||
Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This
|
||||
package modifies that by changing /usr/share/zoneminder/images and
|
||||
/usr/share/zoneminder/events to symlinks to directories under
|
||||
/var/cache/zoneminder.
|
||||
|
||||
There are numerous places these could be put and ways to do it. But, at the
|
||||
moment, if you change this, an upgrade will fail with a warning about these
|
||||
locations having changed (the reason for this was that previously, an upgrade
|
||||
would silently revert the changes and cause event loss - refer
|
||||
bug #608793).
|
||||
|
||||
If you do want to change the location, here are a couple of suggestions.
|
||||
(thanks to vagrant@freegeek.org):
|
||||
|
||||
These lines in fstab could allow you to bind-mount an alternate location
|
||||
|
||||
/dev/sdX1 /otherdrive ext3 defaults 0 2
|
||||
/otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2
|
||||
/otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2
|
||||
|
||||
or if you have a separate partition for each:
|
||||
|
||||
/dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2
|
||||
/dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2
|
||||
|
||||
-- Peter Howard <pjh@northern-ridge.com.au>, Sun, 16 Jan 2010 01:35:51 +1100
|
||||
ZoneMinder is now able to be configured to use an alternative location for storing
|
||||
events and images at compile time. This package makes use of that, so symlinks in
|
||||
/usr/share/zoneminder/www are no longer necessary.
|
||||
|
||||
Access to /dev/video*
|
||||
---------------------
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
zoneminder (1.31.4-vivid1) vivid; urgency=medium
|
||||
|
||||
* Release 1.31.4
|
||||
|
||||
-- Isaac Connor <iconnor@tesla.com> Thu, 21 Sep 2017 09:55:31 -0700
|
||||
|
||||
|
||||
zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low
|
||||
*
|
||||
-- Isaac Connor <iconnor@connortechnology.com> Fri, 23 Feb 2018 14:15:59 -0500
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
|
|||
,libcurl4-gnutls-dev
|
||||
,libgnutls-openssl-dev
|
||||
,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev
|
||||
,libmysqlclient-dev | libmariadbclient-dev
|
||||
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
|
||||
,libpcre3-dev
|
||||
,libpolkit-gobject-1-dev
|
||||
,libv4l-dev (>= 0.8.3) [!hurd-any]
|
||||
|
@ -39,7 +39,7 @@ Package: zoneminder
|
|||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||
,javascript-common
|
||||
,libmp4v2-2, libx264-142|libx264-148, libswscale-ffmpeg3|libswscale4|libswscale3
|
||||
,libmp4v2-2, libx264-142|libx264-148|libx264-152, libswscale-ffmpeg3|libswscale4|libswscale3
|
||||
,ffmpeg | libav-tools
|
||||
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
||||
,libdbd-mysql-perl
|
||||
|
@ -59,9 +59,11 @@ 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
|
||||
,mysql-client | virtual-mysql-client
|
||||
,libnumber-bytes-human-perl
|
||||
,libfile-slurp-perl
|
||||
,mysql-client | mariadb-client | virtual-mysql-client
|
||||
,perl-modules
|
||||
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
|
||||
,policykit-1
|
||||
|
@ -70,7 +72,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,libpcre3
|
||||
Recommends: ${misc:Recommends}
|
||||
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
|
||||
,mysql-server | virtual-mysql-server
|
||||
,mysql-server | mariadb-server | virtual-mysql-server
|
||||
,zoneminder-doc (>= ${source:Version})
|
||||
,ffmpeg
|
||||
Suggests: fcgiwrap, logrotate
|
||||
|
|
|
@ -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"
|
||||
|
@ -66,9 +67,6 @@ override_dh_fixperms:
|
|||
chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
|
||||
chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
|
||||
|
||||
override_dh_installinit:
|
||||
dh_installinit --no-start
|
||||
|
||||
override_dh_systemd_start:
|
||||
dh_systemd_start --no-start
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: zoneminder
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop: $network $remote_fs $syslog
|
||||
# Should-Start: mysql
|
||||
# Should-Stop: mysql
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Control ZoneMinder as a Service
|
||||
# Description: ZoneMinder CCTV recording and surveillance system
|
||||
### END INIT INFO
|
||||
# chkconfig: 2345 20 20
|
||||
|
||||
# Source function library.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
prog=ZoneMinder
|
||||
ZM_PATH_BIN="/usr/bin"
|
||||
RUNDIR="/var/run/zm"
|
||||
TMPDIR="/tmp/zm"
|
||||
command="$ZM_PATH_BIN/zmpkg.pl"
|
||||
|
||||
start() {
|
||||
echo -n "Starting $prog: "
|
||||
export TZ=:/etc/localtime
|
||||
mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR"
|
||||
mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR"
|
||||
$command start
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && echo success
|
||||
[ $RETVAL != 0 ] && echo failure
|
||||
echo
|
||||
[ $RETVAL = 0 ] && touch /var/lock/zm
|
||||
return $RETVAL
|
||||
}
|
||||
stop() {
|
||||
echo -n "Stopping $prog: "
|
||||
#
|
||||
# Why is this status check being done?
|
||||
# as $command stop returns 1 if zoneminder
|
||||
# is stopped, which will result in
|
||||
# this returning 1, which will stuff
|
||||
# dpkg when it tries to stop zoneminder before
|
||||
# uninstalling . . .
|
||||
#
|
||||
result=`$command status`
|
||||
if [ ! "$result" = "running" ]; then
|
||||
echo "Zoneminder already stopped"
|
||||
echo
|
||||
RETVAL=0
|
||||
else
|
||||
$command stop
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && echo success
|
||||
[ $RETVAL != 0 ] && echo failure
|
||||
echo
|
||||
[ $RETVAL = 0 ] && rm -f /var/lock/zm
|
||||
fi
|
||||
}
|
||||
status() {
|
||||
result=`$command status`
|
||||
if [ "$result" = "running" ]; then
|
||||
echo "ZoneMinder is running"
|
||||
RETVAL=0
|
||||
else
|
||||
echo "ZoneMinder is stopped"
|
||||
RETVAL=1
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'restart' | 'force-reload')
|
||||
stop
|
||||
start
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 { start | stop | restart | status }"
|
||||
RETVAL=1
|
||||
;;
|
||||
esac
|
||||
exit $RETVAL
|
|
@ -1,40 +1,65 @@
|
|||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
set +e
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
|
||||
for CONFFILE in /etc/zm/conf.d/*.conf; do
|
||||
. "$CONFFILE"
|
||||
done
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
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" ]; then
|
||||
if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then
|
||||
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
|
||||
a2enmod cgi
|
||||
fi
|
||||
|
||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||
|
||||
if [ -e "/etc/init.d/mysql" ]; then
|
||||
# Do this every time the package is installed or upgraded
|
||||
if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; then
|
||||
# Ensure zoneminder is stopped
|
||||
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||
|
||||
|
||||
#
|
||||
# Get mysql started if it isn't
|
||||
# Get mysql started if it isn't running
|
||||
#
|
||||
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
|
||||
if [ -e "/lib/systemd/system/mariadb.service" ]; then
|
||||
DBSERVICE="mariadb.service"
|
||||
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,"
|
||||
echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! systemctl is-active --quiet mysql.service mariadb.service; then
|
||||
# Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service
|
||||
# However, mariadb.service will not return the status of mysql.service.
|
||||
deb-systemd-invoke start $DBSERVICE
|
||||
fi
|
||||
|
||||
# Make sure systemctl status exit code is 0; i.e. the DB is running
|
||||
if systemctl is-active --quiet "$DBSERVICE"; 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
|
||||
|
||||
|
@ -43,20 +68,20 @@ if [ "$1" = "configure" ]; then
|
|||
|
||||
# Add any new PTZ control configurations to the database (will not overwrite)
|
||||
zmcamtool.pl --import >/dev/null 2>&1
|
||||
|
||||
else
|
||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
fi
|
||||
else
|
||||
echo 'mysql not found, assuming remote server.'
|
||||
echo 'MySQL/MariaDB not found; assuming remote server.'
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
|
||||
fi
|
||||
|
||||
echo "Done Updating, starting ZoneMinder"
|
||||
deb-systemd-invoke restart zoneminder.service || exit $?
|
||||
echo "Done Updating; starting ZoneMinder."
|
||||
deb-systemd-invoke restart zoneminder.service
|
||||
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
||||
|
|
|
@ -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
|
||||
|
|
58
docs/api.rst
58
docs/api.rst
|
@ -98,15 +98,15 @@ This command will add a new http monitor.
|
|||
|
||||
::
|
||||
|
||||
curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton \
|
||||
&Monitor[Function]=Modect \
|
||||
&Monitor[Protocol]=http \
|
||||
&Monitor[Method]=simple \
|
||||
&Monitor[Host]=usr:pass@192.168.11.20 \
|
||||
&Monitor[Port]=80 \
|
||||
&Monitor[Path]=/mjpg/video.mjpg \
|
||||
&Monitor[Width]=704 \
|
||||
&Monitor[Height]=480 \
|
||||
curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton\
|
||||
&Monitor[Function]=Modect\
|
||||
&Monitor[Protocol]=http\
|
||||
&Monitor[Method]=simple\
|
||||
&Monitor[Host]=usr:pass@192.168.11.20\
|
||||
&Monitor[Port]=80\
|
||||
&Monitor[Path]=/mjpg/video.mjpg\
|
||||
&Monitor[Width]=704\
|
||||
&Monitor[Height]=480\
|
||||
&Monitor[Colours]=4"
|
||||
|
||||
Edit monitor 1
|
||||
|
@ -304,26 +304,26 @@ Create a Zone
|
|||
|
||||
::
|
||||
|
||||
curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted \
|
||||
&Zone[MonitorId]=3 \
|
||||
&Zone[Type]=Active \
|
||||
&Zone[Units]=Percent \
|
||||
&Zone[NumCoords]=4 \
|
||||
&Zone[Coords]=0,0 639,0 639,479 0,479 \
|
||||
&Zone[AlarmRGB]=16711680 \
|
||||
&Zone[CheckMethod]=Blobs \
|
||||
&Zone[MinPixelThreshold]=25 \
|
||||
&Zone[MaxPixelThreshold]= \
|
||||
&Zone[MinAlarmPixels]=9216 \
|
||||
&Zone[MaxAlarmPixels]= \
|
||||
&Zone[FilterX]=3 \
|
||||
&Zone[FilterY]=3 \
|
||||
&Zone[MinFilterPixels]=9216 \
|
||||
&Zone[MaxFilterPixels]=230400 \
|
||||
&Zone[MinBlobPixels]=6144 \
|
||||
&Zone[MaxBlobPixels]= \
|
||||
&Zone[MinBlobs]=1 \
|
||||
&Zone[MaxBlobs]= \
|
||||
curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted\
|
||||
&Zone[MonitorId]=3\
|
||||
&Zone[Type]=Active\
|
||||
&Zone[Units]=Percent\
|
||||
&Zone[NumCoords]=4\
|
||||
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
||||
&Zone[AlarmRGB]=16711680\
|
||||
&Zone[CheckMethod]=Blobs\
|
||||
&Zone[MinPixelThreshold]=25\
|
||||
&Zone[MaxPixelThreshold]=\
|
||||
&Zone[MinAlarmPixels]=9216\
|
||||
&Zone[MaxAlarmPixels]=\
|
||||
&Zone[FilterX]=3\
|
||||
&Zone[FilterY]=3\
|
||||
&Zone[MinFilterPixels]=9216\
|
||||
&Zone[MaxFilterPixels]=230400\
|
||||
&Zone[MinBlobPixels]=6144\
|
||||
&Zone[MaxBlobPixels]=\
|
||||
&Zone[MinBlobs]=1\
|
||||
&Zone[MaxBlobs]=\
|
||||
&Zone[OverloadFrames]=0"
|
||||
|
||||
PTZ Control APIs
|
||||
|
|
34
docs/faq.rst
34
docs/faq.rst
|
@ -71,22 +71,23 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun
|
|||
|
||||
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
|
||||
::
|
||||
1280*960 = 1,228,800 (bits)
|
||||
1,228,800 * 24 = 2,359,296,000 (bits)
|
||||
2,359,296,000 * 50 = 5,898,240,000 (bits)
|
||||
5,898,240,000 * 4 = 7,077,888,000 (bits)
|
||||
7,077,888,000 / 8 = 884,736,000 (bytes)
|
||||
884,736,000 / 1000 = 884,736 (Kilobytes)
|
||||
884,736 / 1000 = 864 (Megabytes)
|
||||
864 / 1000 = 0.9 (Gigabyte)
|
||||
|
||||
Around 900MB of memory.
|
||||
1280*960 = 1,228,800 (bytes)
|
||||
1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes)
|
||||
3,686,400 * 50 = 184,320,000 (bytes)
|
||||
184,320,000 * 4 = 737,280,000 (bytes)
|
||||
737,280,000 / 1024 = 720,000 (Kilobytes)
|
||||
720,000 / 1024 = 703.125 (Megabytes)
|
||||
703.125 / 1024 = 0.686 (Gigabytes)
|
||||
|
||||
Around 700MB of memory.
|
||||
|
||||
So if you have 2GB of memory, you should be all set. Right? **Not, really**:
|
||||
|
||||
* This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in.
|
||||
* You also need to account for other processes not related to ZM running in your box
|
||||
* You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory
|
||||
* If you are using H264 encoding, that buffers a lot of frames in memory as well.
|
||||
|
||||
So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well)
|
||||
|
||||
|
@ -128,15 +129,14 @@ So, for example:
|
|||
::
|
||||
|
||||
384x288 capture resolution, that makes: 110 592 pixels
|
||||
in 24 bit color that's x24 = 2 654 208 bits per frame
|
||||
by 80 frames ring buffer x80 = 212 336 640 bits per camera
|
||||
by 4 cameras x4 = 849 346 560 bits.
|
||||
Plus 10% overhead = 934 281 216 bits
|
||||
That's 116 785 152 bytes, and
|
||||
= 114 048 kB, respectively 111.38 MB.
|
||||
If my shared memory is set to 134 217 728, which is exactly 128MB,
|
||||
in 24 bit color that's x 3 = 331,776 bytes per frame
|
||||
by 80 frames ring buffer x80 = 26,542,080 bytes per camera
|
||||
by 4 cameras x4 = 106,168,320 bytes.
|
||||
Plus 10% overhead = 116,785,152 bytes
|
||||
Thats 114,048 kB, respectively 111.38 MB.
|
||||
If my shared memory is set to 134,217,728, which is exactly 128MB,
|
||||
that means I shouldn't have any problem.
|
||||
(Note that 1 byte = 8 bits and 1kbyte = 1024bytes, 1MB = 1024 kB)
|
||||
(Note that 1kbyte = 1024bytes, 1MB = 1024 kB)
|
||||
|
||||
If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here <http://www.redhat.com/docs/manuals/database/RHDB-2.1-Manual/admin_user/kernel-resources.html>`__
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
Dedicated Drive, Partition, or Network Share
|
||||
============================================
|
||||
|
||||
One of the first steps the end user must perform after installing ZoneMinder is to dedicate an entire partition, drive, or network share for ZoneMinder's event storage.
|
||||
The reason being, ZoneMinder will, by design, fill up your hard disk, and you don't want to do that to your root volume!
|
||||
|
||||
The following steps apply to ZoneMinder 1.31 or newer, running on a typical Linux system, which uses systemd.
|
||||
If you are using an older version of ZoneMinder, please follow the legacy steps in the `ZoneMinder Wiki <https://wiki.zoneminder.com/Using_a_dedicated_Hard_Drive>`_.
|
||||
|
||||
**Step 1:** Stop ZoneMinder
|
||||
::
|
||||
|
||||
sudo systemctl stop zoneminder
|
||||
|
||||
**Step 2:** Mount your dedicated drive, partition, or network share to the local filesystem in any folder of your choosing.
|
||||
We recommend you use systemd to manage the mount points.
|
||||
Instructions on how to accomplish this can be found `here <https://zoneminder.blogspot.com/p/blog-page.html>`__ and `here <https://wiki.zoneminder.com/Common_Issues_with_Zoneminder_Installation_on_Ubuntu#Use_Systemd_to_Mount_Internal_Drive_or_NAS>`__.
|
||||
Note that bind mounting ZoneMinder's images folder is optional. Newer version of ZoneMinder write very little, if anything, to the images folder.
|
||||
Verify the dedicated drive, partition, or network share is successfully mounted before proceeding to the next step.
|
||||
|
||||
**Step 3:** Set the owner and group to that of the web server user account. Debian based distros typically use "www-data" as the web server user account while many rpm based distros use "apache".
|
||||
::
|
||||
|
||||
sudo chown -R apache:apache /path/to/your/zoneminder/events/folder
|
||||
sudo chown -R apache:apache /path/to/your/zoneminder/images/folder
|
||||
|
||||
Recall from the previous step, the images folder is optional.
|
||||
|
||||
**Step 4:** Create a config file under /etc/zm/conf.d using your favorite text editor. Name the file anything you want just as long as it ends in ".conf".
|
||||
Add the following content to the file and save your changes:
|
||||
::
|
||||
|
||||
ZM_DIR_EVENTS=/full/path/to/the/events/folder
|
||||
ZM_DIR_IMAGES=/full/path/to/the/images/folder
|
||||
|
||||
**Step 5:** Start ZoneMinder and inspect the ZoneMinder log files for errors.
|
||||
::
|
||||
|
||||
sudo systemctl start zoneminder
|
|
@ -11,3 +11,4 @@ Contents:
|
|||
debian
|
||||
redhat
|
||||
multiserver
|
||||
dedicateddrive
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
All Distros - A Simpler Way to Build ZoneMinder
|
||||
All Distros - A Docker Way to Build ZoneMinder
|
||||
===============================================
|
||||
|
||||
.. contents::
|
||||
|
@ -27,6 +27,8 @@ Procedure
|
|||
|
||||
- If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here.
|
||||
|
||||
- If the desired distro architecture is arm, refer to `Appendix A - Enable Qemu On the Host`_ to enable qemu emulation on your amd64 host machine.
|
||||
|
||||
**Step 2:** Install Docker.
|
||||
|
||||
You need to have a working installation of Docker so head over to the `Docker site <https://docs.docker.com/engine/installation/>`_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following:
|
||||
|
@ -44,7 +46,7 @@ Clone the ZoneMinder project if you have not done so already.
|
|||
|
||||
::
|
||||
|
||||
git clone https://ZoneMinder/ZoneMinder
|
||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||
cd ZoneMinder
|
||||
|
||||
Alternatively, if you have already cloned the repo and wish to update it, do the following.
|
||||
|
@ -99,7 +101,27 @@ For advanced users who really want to go out into uncharted waters, it is theore
|
|||
|
||||
Building arm packages in this manner has not been tested by us, however.
|
||||
|
||||
Appendix A - Enable Qemu On the Host
|
||||
------------------------------------
|
||||
|
||||
If you intend to build ZoneMinder packages for arm on an amd64 host, then Debian users can following these steps to enable transparent Qemu emulation:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install binfmt-support qemu qemu-user-static
|
||||
|
||||
Verify arm emulation is enabled by issuing:
|
||||
|
||||
::
|
||||
|
||||
sudo update-binfmts --enable qemu-arm
|
||||
|
||||
You may get a message stating emulation for this processor is already enabled.
|
||||
|
||||
More testing needs to be done for Redhat distros but it appears Fedora users can just run:
|
||||
|
||||
::
|
||||
|
||||
sudo systemctl start systemd-binfmt
|
||||
|
||||
TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome.
|
||||
|
|
|
@ -45,9 +45,7 @@ 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:
|
||||
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
|
||||
-----------------------------------------
|
||||
|
||||
|
@ -74,9 +65,9 @@ The feedback we get from those who use these development packages is extremely h
|
|||
How to Change from Zmrepo to RPM Fusion
|
||||
---------------------------------------
|
||||
|
||||
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
|
||||
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`__. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
|
||||
|
||||
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`_ and enable RPM Fusion on your system
|
||||
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`__ and enable RPM Fusion on your system
|
||||
- Now issue the following from the command line:
|
||||
|
||||
::
|
||||
|
@ -132,7 +123,7 @@ Your build environment is now set up.
|
|||
|
||||
Build from SRPM
|
||||
***************
|
||||
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <https://rpmfusion.org/>`_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
|
||||
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <https://rpmfusion.org/>`__. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
|
||||
|
||||
For this example, I'll use one of the source rpms from zmrepo:
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Viewing Monitors
|
||||
================
|
||||
|
||||
ZoneMinder allows you to view a live feed of your configured monitors. Once can access this view by clicking on the "Name" column of any of the monitors
|
||||
ZoneMinder allows you to view a live feed of your configured monitors. One can access this view by clicking on the "Name" column of any of the monitors
|
||||
|
||||
.. image:: images/viewmonitor-main.png
|
||||
:width: 500px
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -400,7 +400,22 @@ our @options = (
|
|||
type => $types{boolean},
|
||||
category => 'system',
|
||||
},
|
||||
# PP - Google reCaptcha settings
|
||||
{
|
||||
name => 'ZM_OPT_USE_EVENTNOTIFICATION',
|
||||
default => 'no',
|
||||
description => 'Enable 3rd party Event Notification Server',
|
||||
help => q`
|
||||
zmeventnotification is a 3rd party event notification server
|
||||
that is used to get notifications for alarms detected by ZoneMinder
|
||||
in real time. zmNinja requires this server for push notifications to
|
||||
mobile phones. This option only enables the server if its already installed.
|
||||
Please visit https://github.com/pliablepixels/zmeventserver for installation
|
||||
instructions.
|
||||
`,
|
||||
type => $types{boolean},
|
||||
category => 'system',
|
||||
},
|
||||
# Google reCaptcha settings
|
||||
{
|
||||
name => 'ZM_OPT_USE_GOOG_RECAPTCHA',
|
||||
default => 'no',
|
||||
|
@ -424,7 +439,6 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_OPT_GOOG_RECAPTCHA_SITEKEY',
|
||||
default => '...Insert your recaptcha site-key here...',
|
||||
description => 'Your recaptcha site-key',
|
||||
help => q`You need to generate your keys from
|
||||
the Google reCaptcha website.
|
||||
|
@ -1709,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',
|
||||
|
@ -2952,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',
|
||||
|
|
|
@ -47,9 +47,9 @@ sub new {
|
|||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = {};
|
||||
$self->{name} = "PelcoD";
|
||||
$self->{name} = $class;
|
||||
if ( !defined($id) ) {
|
||||
Fatal( "No monitor defined when invoking protocol ".$self->{name} );
|
||||
Fatal('No monitor defined when invoking protocol '.$self->{name});
|
||||
}
|
||||
$self->{id} = $id;
|
||||
bless( $self, $class );
|
||||
|
@ -61,14 +61,13 @@ sub DESTROY {
|
|||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $class = ref($self) || Fatal("$self not object");
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
if ( exists($self->{$name}) ) {
|
||||
return $self->{$name};
|
||||
}
|
||||
croak( "Can't access $name member of object of class $class" );
|
||||
Error("Can't access $name member of object of class $class");
|
||||
}
|
||||
|
||||
sub getKey {
|
||||
|
@ -83,7 +82,8 @@ sub open {
|
|||
|
||||
sub close {
|
||||
my $self = shift;
|
||||
Fatal( "No close method defined for protocol ".$self->{name} );
|
||||
$self->{state} = 'closed';
|
||||
Debug('No close method defined for protocol '.$self->{name});
|
||||
}
|
||||
|
||||
sub loadMonitor {
|
||||
|
|
|
@ -50,31 +50,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -88,12 +63,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -82,12 +57,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder D-Link DCS-3415 IP Control Protocol Module, 2018-03-04, 0.1
|
||||
# Copyright (C) 2015-2018 Habib Kamei
|
||||
#
|
||||
# Modified for use with D-Link DCS-3415 IP Camera by Habib Kamei
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the implementation of the D-Link DCS-3415 device control protocol
|
||||
#
|
||||
#===========================================================================
|
||||
|
||||
package ZoneMinder::Control::DCS3415;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# D-Link DCS-3415 Control Protocol
|
||||
#
|
||||
# On ControlAddress use the format :
|
||||
# USERNAME:PASSWORD@ADDRESS:PORT
|
||||
# eg : admin:@10.1.2.1:80
|
||||
# zoneuser:zonepass@192.168.0.20:40000
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $result = undef;
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/viewer/$cmd" );
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
|
||||
if ( $res->is_success )
|
||||
{
|
||||
$result = !undef;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Error check failed:'".$res->status_line()."'" );
|
||||
}
|
||||
|
||||
return( $result );
|
||||
}
|
||||
|
||||
#Zoom In
|
||||
sub Tele
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Zoom Tele" );
|
||||
my $cmd = "camctrl.cgi?channel=0&camid=1&zoom=tele";
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
#Zoom Out
|
||||
sub Wide
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Zoom Wide" );
|
||||
my $cmd = "camctrl.cgi?channel=0&camid=1&zoom=wide";
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
#Focus Near
|
||||
sub Near
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Focus Near" );
|
||||
my $cmd = "camctrl.cgi?channel=0&camid=1&focus=near";
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
#Focus Far
|
||||
sub Far
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Focus Far" );
|
||||
my $cmd = "camctrl.cgi?channel=0&camid=1&focus=far";
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
# Below is stub documentation for your module. You'd better edit it!
|
||||
=head1 NAME
|
||||
ZoneMinder::Database - Perl extension for blah blah blah
|
||||
=head1 SYNOPSIS
|
||||
use ZoneMinder::Database;
|
||||
blah blah blah
|
||||
=head1 DESCRIPTION
|
||||
Stub documentation for ZoneMinder, created by h2xs. It looks like the
|
||||
author of the extension was negligent enough to leave the stub
|
||||
unedited.
|
||||
Blah blah blah.
|
||||
=head2 EXPORT
|
||||
None by default.
|
||||
=head1 SEE ALSO
|
||||
Mention other useful documentation such as the documentation of
|
||||
related modules or operating system documentation (such as man pages
|
||||
in UNIX), or any relevant external documentation such as RFCs or
|
||||
standards.
|
||||
If you have a mailing list set up for your module, mention it here.
|
||||
If you have a web site set up for your module, mention it here.
|
||||
=head1 AUTHOR
|
||||
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
Copyright (C) 2001-2008 Philip Coombes
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.8.3 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
=cut
|
|
@ -79,31 +79,6 @@ use Time::HiRes qw( usleep );
|
|||
# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works...
|
||||
my $osd = "on";
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -114,12 +89,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -85,31 +85,6 @@ use Time::HiRes qw( usleep );
|
|||
# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works...
|
||||
my $osd = "on";
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -120,12 +95,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -80,12 +80,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -63,33 +63,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
our $stop_command;
|
||||
|
||||
sub open
|
||||
|
|
|
@ -94,12 +94,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $msg = shift;
|
||||
|
|
|
@ -55,31 +55,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
our $stop_command;
|
||||
|
||||
sub open
|
||||
|
@ -94,12 +69,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -53,158 +53,136 @@ use HTTP::Cookies;
|
|||
my $ChannelID = 1; # Usually...
|
||||
my $DefaultFocusSpeed = 50; # Should be between 1 and 100
|
||||
my $DefaultIrisSpeed = 50; # Should be between 1 and 100
|
||||
my ($user,$pass,$host,$port);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
sub open {
|
||||
my $self = shift;
|
||||
$self->loadMonitor();
|
||||
#
|
||||
# Create a UserAgent for the requests
|
||||
#
|
||||
$self->{UA} = LWP::UserAgent->new();
|
||||
$self->{UA}->cookie_jar( {} );
|
||||
#
|
||||
# Extract the username/password host/port from ControlAddress
|
||||
#
|
||||
my ($user,$pass,$host,$port);
|
||||
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) { # user:pass@host...
|
||||
$user = $1;
|
||||
$pass = $2;
|
||||
$host = $3;
|
||||
}
|
||||
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ ) { # user@host...
|
||||
$user = $1;
|
||||
$host = $2;
|
||||
}
|
||||
else { # Just a host
|
||||
$host = $self->{Monitor}{ControlAddress};
|
||||
}
|
||||
# Check if it is a host and port or just a host
|
||||
if( $host =~ /([^:]+):(.+)/ ) {
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
}
|
||||
else {
|
||||
$port = 80;
|
||||
}
|
||||
# Save the credentials
|
||||
if( defined($user) ) {
|
||||
$self->{UA}->credentials( "$host:$port", $self->{Monitor}{ControlDevice}, $user, $pass );
|
||||
}
|
||||
# Save the base url
|
||||
$self->{BaseURL} = "http://$host:$port";
|
||||
my $self = shift;
|
||||
$self->loadMonitor();
|
||||
#
|
||||
# Create a UserAgent for the requests
|
||||
#
|
||||
$self->{UA} = LWP::UserAgent->new();
|
||||
$self->{UA}->cookie_jar( {} );
|
||||
#
|
||||
# Extract the username/password host/port from ControlAddress
|
||||
#
|
||||
if ( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) { # user:pass@host...
|
||||
$user = $1;
|
||||
$pass = $2;
|
||||
$host = $3;
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ ) { # user@host...
|
||||
$user = $1;
|
||||
$host = $2;
|
||||
} else { # Just a host
|
||||
$host = $self->{Monitor}{ControlAddress};
|
||||
}
|
||||
# Check if it is a host and port or just a host
|
||||
if ( $host =~ /([^:]+):(.+)/ ) {
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
} else {
|
||||
$port = 80;
|
||||
}
|
||||
# Save the credentials
|
||||
if ( defined($user) ) {
|
||||
$self->{UA}->credentials("$host:$port", $self->{Monitor}{ControlDevice}, $user, $pass);
|
||||
}
|
||||
# Save the base url
|
||||
$self->{BaseURL} = "http://$host:$port";
|
||||
}
|
||||
|
||||
sub PutCmd {
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $content = shift;
|
||||
my $req = HTTP::Request->new(PUT => "$self->{BaseURL}/$cmd");
|
||||
if(defined($content)) {
|
||||
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||
}
|
||||
my $res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# The camera timeouts connections at short intervals. When this
|
||||
# happens the user agent connects again and uses the same auth tokens.
|
||||
# The camera rejects this and asks for another token but the UserAgent
|
||||
# just gives up. Because of this I try the request again and it should
|
||||
# succeed the second time if the credentials are correct.
|
||||
#
|
||||
if($res->code == 401) {
|
||||
$res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# It has failed authentication. The odds are
|
||||
# that the user has set some paramater incorrectly
|
||||
# so check the realm against the ControlDevice
|
||||
# entry and send a message if different
|
||||
#
|
||||
my $auth = $res->headers->www_authenticate;
|
||||
foreach (split(/\s*,\s*/,$auth)) {
|
||||
if( $_ =~ /^realm\s*=\s*"([^"]+)"/i ) {
|
||||
if( $self->{Monitor}{ControlDevice} ne $1 ) {
|
||||
Info "Control Device appears to be incorrect.";
|
||||
Info "Control Device should be set to \"$1\".";
|
||||
Info "Control Device currently set to \"$self->{Monitor}{ControlDevice}\".";
|
||||
}
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $content = shift;
|
||||
my $req = HTTP::Request->new(PUT => "$self->{BaseURL}/$cmd");
|
||||
if ( defined($content) ) {
|
||||
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||
}
|
||||
my $res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# The camera timeouts connections at short intervals. When this
|
||||
# happens the user agent connects again and uses the same auth tokens.
|
||||
# The camera rejects this and asks for another token but the UserAgent
|
||||
# just gives up. Because of this I try the request again and it should
|
||||
# succeed the second time if the credentials are correct.
|
||||
#
|
||||
if ( $res->code == 401 ) {
|
||||
$res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# It has failed authentication. The odds are
|
||||
# that the user has set some parameter incorrectly
|
||||
# so check the realm against the ControlDevice
|
||||
# entry and send a message if different
|
||||
#
|
||||
my $auth = $res->headers->www_authenticate;
|
||||
foreach (split(/\s*,\s*/,$auth)) {
|
||||
if ( $_ =~ /^realm\s*=\s*"([^"]+)"/i ) {
|
||||
if ( $self->{Monitor}{ControlDevice} ne $1 ) {
|
||||
Warning("Control Device appears to be incorrect.
|
||||
Control Device should be set to \"$1\".
|
||||
Control Device currently set to \"$self->{Monitor}{ControlDevice}\".");
|
||||
$self->{Monitor}{ControlDevice} = $1;
|
||||
$self->{UA}->credentials("$host:$port", $self->{Monitor}{ControlDevice}, $user, $pass);
|
||||
return PutCmd($self,$cmd,$content);
|
||||
}
|
||||
}
|
||||
#
|
||||
# Check for username/password
|
||||
#
|
||||
if( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||
Info "Check username/password is correct";
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
||||
Info "No password in Control Address. Should there be one?";
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||
Info "Password but no username in Control Address.";
|
||||
} else {
|
||||
Info "Missing username and password in Control Address.";
|
||||
}
|
||||
Fatal $res->status_line;
|
||||
}
|
||||
#
|
||||
# Check for username/password
|
||||
#
|
||||
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||
Info("Check username/password is correct");
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
||||
Info("No password in Control Address. Should there be one?");
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||
Info("Password but no username in Control Address.");
|
||||
} else {
|
||||
Info("Missing username and password in Control Address.");
|
||||
}
|
||||
Fatal($res->status_line);
|
||||
}
|
||||
else {
|
||||
Fatal $res->status_line;
|
||||
}
|
||||
} else {
|
||||
Fatal($res->status_line);
|
||||
}
|
||||
}
|
||||
} # end unless res->is_success
|
||||
} # end sub putCmd
|
||||
#
|
||||
# The move continuous functions all call moveVector
|
||||
# with the direction to move in. This includes zoom
|
||||
#
|
||||
sub moveVector {
|
||||
my $self = shift;
|
||||
my $pandirection = shift;
|
||||
my $tiltdirection = shift;
|
||||
my $zoomdirection = shift;
|
||||
my $params = shift;
|
||||
my $command; # The ISAPI/PTZ command
|
||||
my $self = shift;
|
||||
my $pandirection = shift;
|
||||
my $tiltdirection = shift;
|
||||
my $zoomdirection = shift;
|
||||
my $params = shift;
|
||||
my $command; # The ISAPI/PTZ command
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Change from microseconds to milliseconds
|
||||
$duration = int($duration/1000);
|
||||
my $momentxml;
|
||||
if( $duration ) {
|
||||
$momentxml = "<Momentary><duration>$duration</duration></Momentary>";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/momentary";
|
||||
}
|
||||
else {
|
||||
$momentxml = "";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/continuous";
|
||||
}
|
||||
# Calculate movement speeds
|
||||
my $x = $pandirection * $self->getParam( $params, 'panspeed', 0 );
|
||||
my $y = $tiltdirection * $self->getParam( $params, 'tiltspeed', 0 );
|
||||
my $z = $zoomdirection * $self->getParam( $params, 'speed', 0 );
|
||||
# Create the XML
|
||||
my $xml = "<PTZData><pan>$x</pan><tilt>$y</tilt><zoom>$z</zoom>$momentxml</PTZData>";
|
||||
# Send it to the camera
|
||||
$self->PutCmd($command,$xml);
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Change from microseconds to milliseconds
|
||||
$duration = int($duration/1000);
|
||||
my $momentxml;
|
||||
if( $duration ) {
|
||||
$momentxml = "<Momentary><duration>$duration</duration></Momentary>";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/momentary";
|
||||
}
|
||||
else {
|
||||
$momentxml = "";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/continuous";
|
||||
}
|
||||
# Calculate movement speeds
|
||||
my $x = $pandirection * $self->getParam( $params, 'panspeed', 0 );
|
||||
my $y = $tiltdirection * $self->getParam( $params, 'tiltspeed', 0 );
|
||||
my $z = $zoomdirection * $self->getParam( $params, 'speed', 0 );
|
||||
# Create the XML
|
||||
my $xml = "<PTZData><pan>$x</pan><tilt>$y</tilt><zoom>$z</zoom>$momentxml</PTZData>";
|
||||
# Send it to the camera
|
||||
$self->PutCmd($command,$xml);
|
||||
}
|
||||
sub moveStop { $_[0]->moveVector( 0, 0, 0, splice(@_,1)); }
|
||||
sub moveConUp { $_[0]->moveVector( 0, 1, 0, splice(@_,1)); }
|
||||
|
@ -221,191 +199,191 @@ sub zoomConWide { $_[0]->moveVector( 0, 0,-1, splice(@_,1)); }
|
|||
# Presets including Home set and clear
|
||||
#
|
||||
sub presetGoto {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset/goto");
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset/goto");
|
||||
}
|
||||
sub presetSet {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
my $xml = "<PTZPreset><id>$preset</id></PTZPreset>";
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset",$xml);
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
my $xml = "<PTZPreset><id>$preset</id></PTZPreset>";
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset",$xml);
|
||||
}
|
||||
sub presetHome {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/homeposition/goto");
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/homeposition/goto");
|
||||
}
|
||||
#
|
||||
# Focus controls all call Focus with a +/- speed
|
||||
#
|
||||
sub Focus {
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
my $xml = "<FocusData><focus>$speed</focus></FocusData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/focus",$xml);
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
my $xml = "<FocusData><focus>$speed</focus></FocusData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/focus",$xml);
|
||||
}
|
||||
sub focusConNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Near {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus(-$DefaultFocusSpeed);
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus(-$DefaultFocusSpeed);
|
||||
}
|
||||
sub focusAbsNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
}
|
||||
sub focusRelNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
}
|
||||
sub focusConFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Far {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus($DefaultFocusSpeed);
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus($DefaultFocusSpeed);
|
||||
}
|
||||
sub focusAbsFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
}
|
||||
sub focusRelFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
}
|
||||
#
|
||||
# Iris controls all call Iris with a +/- speed
|
||||
#
|
||||
sub Iris {
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
|
||||
my $xml = "<IrisData><iris>$speed</iris></IrisData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/iris",$xml);
|
||||
my $xml = "<IrisData><iris>$speed</iris></IrisData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/iris",$xml);
|
||||
}
|
||||
sub irisConClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Close {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
$self->Iris(-$DefaultIrisSpeed);
|
||||
$self->Iris(-$DefaultIrisSpeed);
|
||||
}
|
||||
sub irisAbsClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
}
|
||||
sub irisRelClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
}
|
||||
sub irisConOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Open {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
$self->Iris($DefaultIrisSpeed);
|
||||
$self->Iris($DefaultIrisSpeed);
|
||||
}
|
||||
sub irisAbsOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
}
|
||||
sub irisRelOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
}
|
||||
#
|
||||
# reset (reboot) the device
|
||||
#
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
my $self = shift;
|
||||
|
||||
$self->PutCmd("ISAPI/System/reboot");
|
||||
$self->PutCmd("ISAPI/System/reboot");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
|
|
@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -82,33 +82,6 @@ use ZoneMinder::Logger qw(:all);
|
|||
use ZoneMinder::Config qw(:all);
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -126,12 +99,6 @@ sub open
|
|||
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -54,33 +54,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -94,12 +67,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -50,29 +50,6 @@ use ZoneMinder::Config qw(:all);
|
|||
use Time::HiRes qw( usleep );
|
||||
use URI::Encode qw();
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open {
|
||||
my $self = shift;
|
||||
|
||||
|
@ -85,11 +62,6 @@ sub open {
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg {
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
|
|
|
@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -82,12 +57,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -82,12 +57,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -74,33 +74,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -114,12 +87,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -82,12 +57,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -46,31 +46,6 @@ use Time::HiRes qw( usleep );
|
|||
use constant SYNC => 0xff;
|
||||
use constant COMMAND_GAP => 100000; # In ms
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -91,8 +66,7 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
$self->{port}->close();
|
||||
|
|
|
@ -47,31 +47,6 @@ use constant STX => 0xa0;
|
|||
use constant ETX => 0xaf;
|
||||
use constant COMMAND_GAP => 100000; # In ms
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -0,0 +1,597 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Reolink IP Control Protocol Module, Date: 2016-01-19
|
||||
# Converted for use with Reolink IP Camera by Chris Swertfeger
|
||||
# Copyright (C) 2016 Chris Swertfeger
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the first implementation of the Reolink IP camera control
|
||||
# protocol
|
||||
#
|
||||
package ZoneMinder::Control::Reolink;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
our %CamParams = ();
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# Reolink IP Control Protocol
|
||||
# This script sends ONVIF compliant commands and may work with other cameras
|
||||
# that require authentication
|
||||
#
|
||||
# The script was developed against a RLC-423 and RLC-420.
|
||||
#
|
||||
# Basic preset functions are supported, but more advanced features, which make
|
||||
# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc)
|
||||
# may or may not work.
|
||||
#
|
||||
#
|
||||
# On ControlAddress use the format :
|
||||
# USERNAME:PASSWORD@ADDRESS:PORT
|
||||
# eg : admin:pass@10.1.2.1:8899
|
||||
# admin:password@10.0.100.1:8899
|
||||
#
|
||||
# Use port 8000 by default for Reolink cameras
|
||||
#
|
||||
# Make sure and place a value in the Auto Stop Timeout field.
|
||||
# Recommend starting with a value of 1 second, and adjust accordingly.
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
use MIME::Base64;
|
||||
use Digest::SHA;
|
||||
use DateTime;
|
||||
|
||||
my ($username,$password,$host,$port);
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
#
|
||||
# Extract the username/password host/port from ControlAddress
|
||||
#
|
||||
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ )
|
||||
{ # user:pass@host...
|
||||
$username = $1;
|
||||
$password = $2;
|
||||
$host = $3;
|
||||
}
|
||||
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )
|
||||
{ # user@host...
|
||||
$username = $1;
|
||||
$host = $2;
|
||||
}
|
||||
else { # Just a host
|
||||
$host = $self->{Monitor}{ControlAddress};
|
||||
}
|
||||
# Check if it is a host and port or just a host
|
||||
if( $host =~ /([^:]+):(.+)/ )
|
||||
{
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
}
|
||||
else
|
||||
{
|
||||
$port = 80;
|
||||
}
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $msg = shift;
|
||||
my $content_type = shift;
|
||||
my $result = undef;
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $server_endpoint = "http://".$host.":".$port."/$cmd";
|
||||
my $req = HTTP::Request->new( POST => $server_endpoint );
|
||||
$req->header('content-type' => $content_type);
|
||||
$req->header('Host' => $host.":".$port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'close');
|
||||
$req->content($msg);
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
$result = !undef;
|
||||
} else {
|
||||
Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" );
|
||||
}
|
||||
return( $result );
|
||||
}
|
||||
|
||||
sub getCamParams
|
||||
{
|
||||
my $self = shift;
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
|
||||
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
|
||||
my $req = HTTP::Request->new( POST => $server_endpoint );
|
||||
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
|
||||
$req->header('Host' => $host.":".$port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'Close');
|
||||
$req->content($msg);
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
# We should really use an xml or soap library to parse the xml tags
|
||||
my $content = $res->decoded_content;
|
||||
|
||||
if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
|
||||
$CamParams{$1} = $2;
|
||||
}
|
||||
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
|
||||
$CamParams{$1} = $2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
|
||||
}
|
||||
}
|
||||
|
||||
#autoStop
|
||||
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
|
||||
sub autoStop
|
||||
{
|
||||
my $self = shift;
|
||||
my $autostop = shift;
|
||||
|
||||
if( $autostop ) {
|
||||
Debug( "Auto Stop" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
usleep( $autostop );
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
}
|
||||
|
||||
# Reset the Camera
|
||||
sub reset
|
||||
{
|
||||
Debug( "Camera Reset" );
|
||||
my $self = shift;
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $cmd = "";
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Up Arrow
|
||||
sub moveConUp
|
||||
{
|
||||
Debug( "Move Up" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Down Arrow
|
||||
sub moveConDown
|
||||
{
|
||||
Debug( "Move Down" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Left Arrow
|
||||
sub moveConLeft
|
||||
{
|
||||
Debug( "Move Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Right Arrow
|
||||
sub moveConRight
|
||||
{
|
||||
Debug( "Move Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Zoom In
|
||||
sub zoomConTele
|
||||
{
|
||||
Debug( "Zoom Tele" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Zoom Out
|
||||
sub zoomConWide
|
||||
{
|
||||
Debug( "Zoom Wide" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Up Right Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConUpRight
|
||||
{
|
||||
Debug( "Move Diagonally Up Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Down Right Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConDownRight
|
||||
{
|
||||
Debug( "Move Diagonally Down Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Up Left Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConUpLeft
|
||||
{
|
||||
Debug( "Move Diagonally Up Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Down Left Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConDownLeft
|
||||
{
|
||||
Debug( "Move Diagonally Down Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Stop
|
||||
sub moveStop
|
||||
{
|
||||
Debug( "Move Stop" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Set Camera Preset
|
||||
sub presetSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
Debug( "Set Preset $preset" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Recall Camera Preset
|
||||
sub presetGoto
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
my $num = sprintf("%03d", $preset);
|
||||
$num=~ tr/ /0/;
|
||||
Debug( "Goto Preset $preset" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$num.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Horizontal Patrol
|
||||
#To be determined if this camera supports this feature
|
||||
sub horizontalPatrol
|
||||
{
|
||||
Debug( "Horizontal Patrol" );
|
||||
my $self = shift;
|
||||
my $cmd = '';
|
||||
my $msg ='';
|
||||
my $content_type = '';
|
||||
# $self->sendCmd( $cmd, $msg, $content_type );
|
||||
Error( "PTZ Command not implemented in control script." );
|
||||
}
|
||||
|
||||
#Horizontal Patrol Stop
|
||||
#To be determined if this camera supports this feature
|
||||
sub horizontalPatrolStop
|
||||
{
|
||||
Debug( "Horizontal Patrol Stop" );
|
||||
my $self = shift;
|
||||
my $cmd = '';
|
||||
my $msg ='';
|
||||
my $content_type = '';
|
||||
# $self->sendCmd( $cmd, $msg, $content_type );
|
||||
Error( "PTZ Command not implemented in control script." );
|
||||
}
|
||||
|
||||
# Increase Brightness
|
||||
sub irisAbsOpen
|
||||
{
|
||||
Debug( "Iris $CamParams{'Brightness'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Brightness'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $max = 100;
|
||||
|
||||
$CamParams{'Brightness'} += $step;
|
||||
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
# Decrease Brightness
|
||||
sub irisAbsClose
|
||||
{
|
||||
Debug( "Iris $CamParams{'Brightness'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'brightness'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $min = 0;
|
||||
|
||||
$CamParams{'Brightness'} -= $step;
|
||||
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
# Increase Contrast
|
||||
sub whiteAbsIn
|
||||
{
|
||||
Debug( "Iris $CamParams{'Contrast'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Contrast'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $max = 100;
|
||||
|
||||
$CamParams{'Contrast'} += $step;
|
||||
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
}
|
||||
|
||||
# Decrease Contrast
|
||||
sub whiteAbsOut
|
||||
{
|
||||
Debug( "Iris $CamParams{'Contrast'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Contrast'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $min = 0;
|
||||
|
||||
$CamParams{'Contrast'} -= $step;
|
||||
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -60,33 +60,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
our $stop_command;
|
||||
|
||||
sub open
|
||||
|
@ -101,12 +76,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -45,28 +45,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) ) {
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open {
|
||||
my $self = shift;
|
||||
|
||||
|
@ -79,11 +57,6 @@ sub open {
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg {
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
|
|
|
@ -81,31 +81,6 @@ our $ADDRESS = '';
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -164,12 +139,6 @@ sub open
|
|||
} # end if ! $res->is_success
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -45,31 +45,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -83,12 +58,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -46,31 +46,6 @@ use Time::HiRes qw( usleep );
|
|||
use constant SYNC => 0xff;
|
||||
use constant COMMAND_GAP => 100000; # In ms
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -44,33 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
Debug( "Camera New" );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
Debug( "Camera AUTOLOAD" );
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -70,32 +70,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -109,12 +83,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -52,31 +52,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -90,12 +65,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -44,33 +44,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
Debug( "Camera New" );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
Debug( "Camera AUTOLOAD" );
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -49,33 +49,6 @@ use ZoneMinder::Config qw(:all);
|
|||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -89,12 +62,6 @@ sub open
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
|
|
|
@ -77,39 +77,51 @@ sub zmDbConnect {
|
|||
}
|
||||
my $options = shift;
|
||||
|
||||
if ( ( ! defined( $dbh ) ) or ! $dbh->ping() ) {
|
||||
if ( ( !defined($dbh) ) or ! $dbh->ping() ) {
|
||||
my ( $host, $portOrSocket ) = ( $ZoneMinder::Config::Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
|
||||
my $socket;
|
||||
|
||||
if ( defined($portOrSocket) ) {
|
||||
if ( $portOrSocket =~ /^\// ) {
|
||||
$socket = ";mysql_socket=".$portOrSocket;
|
||||
$socket = ';mysql_socket='.$portOrSocket;
|
||||
} else {
|
||||
$socket = ";host=".$host.";port=".$portOrSocket;
|
||||
$socket = ';host='.$host.';port='.$portOrSocket;
|
||||
}
|
||||
} else {
|
||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||
$socket = ';host='.$Config{ZM_DB_HOST};
|
||||
}
|
||||
|
||||
my $sslOptions = "";
|
||||
my $sslOptions = '';
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = ';'.join(';',
|
||||
"mysql_ssl=1",
|
||||
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
'mysql_ssl=1',
|
||||
'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT},
|
||||
'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
|
||||
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
||||
eval {
|
||||
$dbh = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME}
|
||||
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '')
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
);
|
||||
$dbh->trace( 0 );
|
||||
}
|
||||
return( $dbh );
|
||||
}
|
||||
};
|
||||
if ( !$dbh or $@ ) {
|
||||
Error("Error reconnecting to db: errstr:$DBI::errstr error val:$@");
|
||||
} else {
|
||||
$dbh->{AutoCommit} = 1;
|
||||
Fatal('Can\'t set AutoCommit on in database connection')
|
||||
unless $dbh->{AutoCommit};
|
||||
$dbh->{mysql_auto_reconnect} = 1;
|
||||
Fatal('Can\'t set mysql_auto_reconnect on in database connection')
|
||||
unless $dbh->{mysql_auto_reconnect};
|
||||
$dbh->trace( 0 );
|
||||
} # end if success connecting
|
||||
} # end if ! connected
|
||||
return $dbh;
|
||||
} # end sub zmDbConnect
|
||||
|
||||
sub zmDbDisconnect {
|
||||
if ( defined( $dbh ) ) {
|
||||
|
|
|
@ -35,6 +35,9 @@ require Date::Manip;
|
|||
require File::Find;
|
||||
require File::Path;
|
||||
require File::Copy;
|
||||
require File::Slurp;
|
||||
require File::Basename;
|
||||
require Number::Bytes::Human;
|
||||
|
||||
#our @ISA = qw(ZoneMinder::Object);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -300,8 +303,8 @@ sub GenerateVideo {
|
|||
$frame_rate = $fps;
|
||||
}
|
||||
|
||||
my $width = $self->{MonitorWidth};
|
||||
my $height = $self->{MonitorHeight};
|
||||
my $width = $self->{Width};
|
||||
my $height = $self->{Height};
|
||||
my $video_size = " ${width}x${height}";
|
||||
|
||||
if ( $scale ) {
|
||||
|
@ -347,13 +350,17 @@ sub delete {
|
|||
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n" );
|
||||
return;
|
||||
}
|
||||
if ( ! -e $event->Storage()->Path() ) {
|
||||
Warning("Not deleting event because storage path doesn't exist");
|
||||
return;
|
||||
}
|
||||
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" );
|
||||
$ZoneMinder::Database::dbh->ping();
|
||||
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
$event->lock_and_load();
|
||||
#$event->lock_and_load();
|
||||
|
||||
if ( ! $Config{ZM_OPT_FAST_DELETE} ) {
|
||||
{
|
||||
my $sql = 'DELETE FROM Frames WHERE EventId=?';
|
||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
|
||||
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
|
||||
|
@ -375,19 +382,23 @@ sub delete {
|
|||
$ZoneMinder::Database::dbh->commit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Do it individually to avoid locking up the table for new events
|
||||
{
|
||||
my $sql = 'DELETE FROM Events WHERE Id=?';
|
||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
|
||||
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Error( "Can't execute '$sql': ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
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.');
|
||||
}
|
||||
# Do it individually to avoid locking up the table for new events
|
||||
my $sql = 'DELETE FROM Events WHERE Id=?';
|
||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
|
||||
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Error( "Can't execute '$sql': ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
} # end sub delete
|
||||
|
||||
sub delete_files {
|
||||
|
@ -410,8 +421,34 @@ sub delete_files {
|
|||
if ( $event_path ) {
|
||||
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
|
||||
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
|
||||
|
||||
my $deleted = 0;
|
||||
if ( $$Storage{Type} eq 's3fs' ) {
|
||||
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
|
||||
eval {
|
||||
require Net::Amazon::S3;
|
||||
my $s3 = Net::Amazon::S3->new( {
|
||||
aws_access_key_id => $aws_id,
|
||||
aws_secret_access_key => $aws_secret,
|
||||
( $aws_host ? ( host => $aws_host ) : () ),
|
||||
});
|
||||
my $bucket = $s3->bucket($aws_bucket);
|
||||
if ( ! $bucket ) {
|
||||
Error("S3 bucket $bucket not found.");
|
||||
die;
|
||||
}
|
||||
if ( $bucket->delete_key($event_path) ) {
|
||||
$deleted = 1;
|
||||
} else {
|
||||
Error("Failed to delete from S3:".$s3->err . ": " . $s3->errstr);
|
||||
}
|
||||
};
|
||||
Error($@) if $@;
|
||||
}
|
||||
if ( ! $deleted ) {
|
||||
my $command = "/bin/rm -rf $storage_path/$event_path";
|
||||
ZoneMinder::General::executeShellCommand( $command );
|
||||
ZoneMinder::General::executeShellCommand( $command );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $event->Scheme() eq 'Deep' ) {
|
||||
|
@ -419,7 +456,7 @@ sub delete_files {
|
|||
Debug("Deleting files for Event $$event{Id} from $storage_path/$link_path.");
|
||||
if ( $link_path ) {
|
||||
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
|
||||
unlink( $storage_path.'/'.$link_path ) or Error( "Unable to unlink '$storage_path/$link_path': $!" );
|
||||
unlink($storage_path.'/'.$link_path) or Error( "Unable to unlink '$storage_path/$link_path': $!" );
|
||||
}
|
||||
}
|
||||
} # end sub delete_files
|
||||
|
@ -429,7 +466,7 @@ sub Storage {
|
|||
$_[0]{Storage} = $_[1];
|
||||
}
|
||||
if ( ! $_[0]{Storage} ) {
|
||||
$_[0]{Storage} = new ZoneMinder::Storage( $_[0]{StorageId} );
|
||||
$_[0]{Storage} = new ZoneMinder::Storage($_[0]{StorageId});
|
||||
}
|
||||
return $_[0]{Storage};
|
||||
}
|
||||
|
@ -482,6 +519,24 @@ 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 ( $$NewStorage{Id} == $$self{StorageId} ) {
|
||||
return "Event is already located at " . $NewPath;
|
||||
} 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.
|
||||
|
@ -490,25 +545,12 @@ sub MoveTo {
|
|||
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();
|
||||
|
@ -516,34 +558,97 @@ sub MoveTo {
|
|||
}
|
||||
Debug("Moving event $$self{Id} from $OldPath to $NewPath");
|
||||
|
||||
my $moved = 0;
|
||||
|
||||
if ( $$NewStorage{Type} eq 's3fs' ) {
|
||||
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
|
||||
eval {
|
||||
require Net::Amazon::S3;
|
||||
my $s3 = Net::Amazon::S3->new( {
|
||||
aws_access_key_id => $aws_id,
|
||||
aws_secret_access_key => $aws_secret,
|
||||
( $aws_host ? ( host => $aws_host ) : () ),
|
||||
});
|
||||
my $bucket = $s3->bucket($aws_bucket);
|
||||
if ( ! $bucket ) {
|
||||
Error("S3 bucket $bucket not found.");
|
||||
die;
|
||||
}
|
||||
|
||||
my $event_path = 'events/'.$self->RelativePath();
|
||||
Info("Making dir ectory $event_path/");
|
||||
if ( ! $bucket->add_key( $event_path.'/','' ) ) {
|
||||
die "Unable to add key for $event_path/";
|
||||
}
|
||||
|
||||
my @files = glob("$OldPath/*");
|
||||
Debug("Files to move @files");
|
||||
for my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
my $starttime = time;
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! $size ) {
|
||||
Info("Not moving file with 0 size");
|
||||
}
|
||||
my $file_contents = File::Slurp::read_file($file);
|
||||
if ( ! $file_contents ) {
|
||||
die "Loaded empty file, but it had a size. Giving up";
|
||||
}
|
||||
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if ( ! $bucket->add_key( $filename, $file_contents ) ) {
|
||||
die "Unable to add key for $filename";
|
||||
}
|
||||
my $duration = time - $starttime;
|
||||
Debug("PUT to S3 " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($size/$duration) . "/sec");
|
||||
} # end foreach file.
|
||||
|
||||
$moved = 1;
|
||||
};
|
||||
Error($@) if $@;
|
||||
die $@ if $@;
|
||||
} # end if s3
|
||||
|
||||
my $error = '';
|
||||
File::Path::make_path( $NewPath, {error => \my $err} );
|
||||
if ( @$err ) {
|
||||
for my $diag (@$err) {
|
||||
my ($file, $message) = %$diag;
|
||||
next if $message eq 'File exists';
|
||||
if ($file eq '') {
|
||||
$error .= "general error: $message\n";
|
||||
} else {
|
||||
$error .= "problem making $file: $message\n";
|
||||
if ( ! $moved ) {
|
||||
File::Path::make_path( $NewPath, {error => \my $err} );
|
||||
if ( @$err ) {
|
||||
for my $diag (@$err) {
|
||||
my ($file, $message) = %$diag;
|
||||
next if $message eq 'File exists';
|
||||
if ($file eq '') {
|
||||
$error .= "general error: $message\n";
|
||||
} else {
|
||||
$error .= "problem making $file: $message\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
my @files = glob("$OldPath/*");
|
||||
|
||||
for my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
Debug("Moving file $file to $NewPath");
|
||||
if ( ! File::Copy::copy( $file, $NewPath ) ) {
|
||||
$error .= "Copy failed: for $file to $NewPath: $!";
|
||||
last;
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
} # end foreach file.
|
||||
my @files = glob("$OldPath/*");
|
||||
if ( ! @files ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return "No files to move.";
|
||||
}
|
||||
|
||||
for my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
my $starttime = time;
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! File::Copy::copy( $file, $NewPath ) ) {
|
||||
$error .= "Copy failed: for $file to $NewPath: $!";
|
||||
last;
|
||||
}
|
||||
my $duration = time - $starttime;
|
||||
Debug("Copied " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . ($duration?Number::Bytes::Human::format_bytes($size/$duration):'inf') . "/sec");
|
||||
} # end foreach file.
|
||||
} # end if ! moved
|
||||
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
|
@ -558,8 +663,10 @@ sub MoveTo {
|
|||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
$self->delete_files( $OldStorage );
|
||||
Debug("Committing");
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
$self->delete_files( $OldStorage );
|
||||
Debug("Done deleting files, returning");
|
||||
return $error;
|
||||
} # end sub MoveTo
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ sub find_one {
|
|||
|
||||
sub Execute {
|
||||
my $self = $_[0];
|
||||
my $sql = $self->Sql();
|
||||
my $sql = $self->Sql(undef);
|
||||
|
||||
if ( $self->{HasDiskPercent} ) {
|
||||
my $disk_percent = getDiskPercent( $$self{Storage} ? $$self{Storage}->Path() : () );
|
||||
|
@ -130,9 +130,10 @@ sub Execute {
|
|||
}
|
||||
|
||||
sub Sql {
|
||||
my $self = $_[0];
|
||||
my $self = shift;
|
||||
$$self{Sql} = shift if @_;
|
||||
if ( ! $$self{Sql} ) {
|
||||
my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} );
|
||||
my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query});
|
||||
my $sql = 'SELECT E.*,
|
||||
unix_timestamp(E.StartTime) as Time,
|
||||
M.Name as MonitorName,
|
||||
|
@ -159,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/ ) {
|
||||
$self->{Sql} .= 'S.'.$term->{attr};
|
||||
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= 'S.ServerId';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= 'E.StartTime';
|
||||
|
@ -171,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 )';
|
||||
|
@ -205,9 +209,9 @@ sub Sql {
|
|||
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
||||
if ( $term->{attr} =~ /^Monitor/ ) {
|
||||
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}'";
|
||||
|
@ -240,7 +244,7 @@ sub Sql {
|
|||
}
|
||||
$value = "'$value'";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
|
@ -276,7 +280,13 @@ sub Sql {
|
|||
} elsif ( $term->{op} eq '!~' ) {
|
||||
$self->{Sql} .= " not regexp $value";
|
||||
} elsif ( $term->{op} eq 'IS' ) {
|
||||
$self->{Sql} .= " IS $value";
|
||||
if ( $value eq 'Odd' ) {
|
||||
$self->{Sql} .= ' % 2 = 1';
|
||||
} elsif ( $value eq 'Even' ) {
|
||||
$self->{Sql} .= ' % 2 = 0';
|
||||
} else {
|
||||
$self->{Sql} .= " IS $value";
|
||||
}
|
||||
} elsif ( $term->{op} eq 'IS NOT' ) {
|
||||
$self->{Sql} .= " IS NOT $value";
|
||||
} elsif ( $term->{op} eq '=[]' ) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# ZoneMinder Logger Module, $Date$, $Revision$
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
|
@ -17,7 +17,7 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# This module contains the debug definitions and functions used by the rest
|
||||
# of the ZoneMinder scripts
|
||||
|
@ -81,11 +81,11 @@ our @EXPORT = qw();
|
|||
|
||||
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# Logger Facilities
|
||||
#
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
|
@ -158,6 +158,7 @@ sub new {
|
|||
( $this->{fileName} = $0 ) =~ s|^.*/||;
|
||||
$this->{logPath} = $Config{ZM_PATH_LOGS};
|
||||
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
|
||||
($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/;
|
||||
|
||||
$this->{trace} = 0;
|
||||
|
||||
|
@ -207,6 +208,7 @@ sub initialise( @ ) {
|
|||
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
|
||||
$tempLogFile = $logFile;
|
||||
}
|
||||
($tempLogFile) = $tempLogFile =~ /^([\w\.\/]+)$/;
|
||||
|
||||
my $tempLevel = INFO;
|
||||
my $tempTermLevel = $this->{termLevel};
|
||||
|
@ -427,54 +429,10 @@ sub databaseLevel {
|
|||
if ( $this->{databaseLevel} != $databaseLevel ) {
|
||||
if ( $databaseLevel > NOLOG and $this->{databaseLevel} <= NOLOG ) {
|
||||
if ( !$this->{dbh} ) {
|
||||
my $socket;
|
||||
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
|
||||
|
||||
if ( defined($portOrSocket) ) {
|
||||
if ( $portOrSocket =~ /^\// ) {
|
||||
$socket = ';mysql_socket='.$portOrSocket;
|
||||
} else {
|
||||
$socket = ';host='.$host.';port='.$portOrSocket;
|
||||
}
|
||||
} else {
|
||||
$socket = ';host='.$Config{ZM_DB_HOST};
|
||||
}
|
||||
my $sslOptions = '';
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = join(';','',
|
||||
'mysql_ssl=1',
|
||||
'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT},
|
||||
'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
$this->{dbh} = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME}
|
||||
.$socket.$sslOptions
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
);
|
||||
if ( !$this->{dbh} ) {
|
||||
$databaseLevel = NOLOG;
|
||||
Error( 'Unable to write log entries to DB, can\'t connect to database '
|
||||
.$Config{ZM_DB_NAME}
|
||||
.' on host '
|
||||
.$Config{ZM_DB_HOST}
|
||||
);
|
||||
} else {
|
||||
$this->{dbh}->{AutoCommit} = 1;
|
||||
Fatal('Can\'t set AutoCommit on in database connection' )
|
||||
unless( $this->{dbh}->{AutoCommit} );
|
||||
$this->{dbh}->{mysql_auto_reconnect} = 1;
|
||||
Fatal('Can\'t set mysql_auto_reconnect on in database connection' )
|
||||
unless( $this->{dbh}->{mysql_auto_reconnect} );
|
||||
$this->{dbh}->trace( 0 );
|
||||
}
|
||||
$this->{dbh} = ZoneMinder::Database::zmDbConnect();
|
||||
}
|
||||
} elsif ( $databaseLevel <= NOLOG && $this->{databaseLevel} > NOLOG ) {
|
||||
if ( $this->{dbh} ) {
|
||||
$this->{dbh}->disconnect();
|
||||
undef($this->{dbh});
|
||||
}
|
||||
undef($this->{dbh});
|
||||
}
|
||||
$this->{databaseLevel} = $databaseLevel;
|
||||
}
|
||||
|
@ -581,28 +539,34 @@ sub logPrint {
|
|||
syslog($priorities{$level}, $code.' [%s]', $string);
|
||||
}
|
||||
print($LOGFILE $message) if $level <= $this->{fileLevel};
|
||||
print(STDERR $message) if $level <= $this->{termLevel};
|
||||
|
||||
if ( $level <= $this->{databaseLevel} ) {
|
||||
my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
|
||||
$this->{sth} = $this->{dbh}->prepare_cached($sql);
|
||||
if ( !$this->{sth} ) {
|
||||
$this->{databaseLevel} = NOLOG;
|
||||
Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr());
|
||||
} else {
|
||||
my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0)
|
||||
, $this->{id}
|
||||
, $$
|
||||
, $level
|
||||
, $code
|
||||
, $string
|
||||
, $this->{fileName}
|
||||
);
|
||||
if ( !$res ) {
|
||||
if ( ( $this->{dbh} and $this->{dbh}->ping() ) or ( $this->{dbh} = ZoneMinder::Database::zmDbConnect() ) ) {
|
||||
|
||||
my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
|
||||
$this->{sth} = $this->{dbh}->prepare_cached($sql);
|
||||
if ( !$this->{sth} ) {
|
||||
$this->{databaseLevel} = NOLOG;
|
||||
Error("Can't execute log entry '$sql': ".$this->{sth}->errstr());
|
||||
Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr());
|
||||
} else {
|
||||
my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0)
|
||||
, $this->{id}
|
||||
, $$
|
||||
, $level
|
||||
, $code
|
||||
, $string
|
||||
, $this->{fileName}
|
||||
);
|
||||
if ( !$res ) {
|
||||
$this->{databaseLevel} = NOLOG;
|
||||
Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print(STDERR "Can't log to database: ");
|
||||
}
|
||||
} # end if doing db logging
|
||||
print(STDERR $message) if $level <= $this->{termLevel};
|
||||
} # end if level < effectivelevel
|
||||
}
|
||||
|
||||
|
|
|
@ -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++ },
|
||||
|
@ -166,6 +166,7 @@ our $mem_data = {
|
|||
last_write_time => { type=>'time_t64', seq=>$mem_seq++ },
|
||||
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'=> {
|
||||
|
@ -315,6 +316,12 @@ sub zmMemRead {
|
|||
my $type = $mem_data->{$section}->{contents}->{$element}->{type};
|
||||
my $size = $mem_data->{$section}->{contents}->{$element}->{size};
|
||||
|
||||
if (!defined $offset || !defined $type || !defined $size) {
|
||||
Error ("Invalid field:".$field." setting to undef and exiting zmMemRead");
|
||||
zmMemInvalidate( $monitor );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
my $data = zmMemGet( $monitor, $offset, $size );
|
||||
if ( !defined($data) ) {
|
||||
Error( "Unable to read '$field' from memory for monitor ".$monitor->{Id} );
|
||||
|
@ -820,6 +827,7 @@ colour Read/write location for the current monitor colour
|
|||
contrast Read/write location for the current monitor contrast
|
||||
alarm_x Image x co-ordinate (from left) of the centre of the last motion event, -1 if none
|
||||
alarm_y Image y co-ordinate (from top) of the centre of the last motion event, -1 if none
|
||||
alarm_cause The current alarm event cause string along with zone names(s) alarmed
|
||||
|
||||
trigger_data The triggered event mapped memory section
|
||||
size The size, in bytes of this section
|
||||
|
|
|
@ -125,6 +125,7 @@ sub load {
|
|||
if ( $data and %$data ) {
|
||||
@$self{keys %$data} = values %$data;
|
||||
} # end if
|
||||
return $data;
|
||||
} # end sub load
|
||||
|
||||
sub lock_and_load {
|
||||
|
@ -134,33 +135,34 @@ sub lock_and_load {
|
|||
no strict 'refs';
|
||||
my $table = ${$type.'::table'};
|
||||
if ( ! $table ) {
|
||||
Error( 'NO table for type ' . $type );
|
||||
Error('NO table for type ' . $type);
|
||||
return;
|
||||
} # end if
|
||||
my $primary_key = ${$type.'::primary_key'};
|
||||
if ( ! $primary_key ) {
|
||||
Error( 'NO primary_key for type ' . $type );
|
||||
Error('NO primary_key for type ' . $type);
|
||||
return;
|
||||
} # end if
|
||||
|
||||
if ( ! $$self{$primary_key} ) {
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
Error( (ref $self) . "::lock_and_load called without $primary_key from $caller:$line");
|
||||
Error("$type ::lock_and_load called without $primary_key from $caller:$line");
|
||||
return;
|
||||
|
||||
}
|
||||
#$log->debug("Object::load Loading from db $type");
|
||||
Debug("Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
|
||||
my $data = $ZoneMinder::Database::dbh->selectrow_hashref( "SELECT * FROM $table WHERE $primary_key=? FOR UPDATE", {}, $$self{$primary_key} );
|
||||
|
||||
Debug("Lock and Load $type from $table WHERE $primary_key = $$self{$primary_key}");
|
||||
my $data = $ZoneMinder::Database::dbh->selectrow_hashref("SELECT * FROM $table WHERE $primary_key=? FOR UPDATE", {}, $$self{$primary_key});
|
||||
if ( ! $data ) {
|
||||
if ( $ZoneMinder::Database::dbh->errstr ) {
|
||||
Error( "Failure to load Object record for $$self{$primary_key}: Reason: " . $ZoneMinder::Database::dbh->errstr );
|
||||
Error("Failure to load Object record for $$self{$primary_key}: Reason: " . $ZoneMinder::Database::dbh->errstr);
|
||||
} else {
|
||||
Debug("No Results Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
|
||||
Debug("No Results Lock and Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
|
||||
} # end if
|
||||
} # end if
|
||||
if ( $data and %$data ) {
|
||||
@$self{keys %$data} = values %$data;
|
||||
} else {
|
||||
Debug("No values Lock and Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
|
||||
} # end if
|
||||
} # end sub lock_and_load
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -246,7 +248,7 @@ MAIN: while( $loop ) {
|
|||
|
||||
{
|
||||
my @day_dirs = glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]");
|
||||
Debug(qq`Checking for Deep Events under using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . " events");
|
||||
Debug(qq`Checking for Deep Events under $$Storage{Path} using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . ' events');
|
||||
foreach my $day_dir ( @day_dirs ) {
|
||||
Debug( "Checking day dir $day_dir" );
|
||||
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
|
||||
|
@ -294,7 +296,7 @@ MAIN: while( $loop ) {
|
|||
} # end foreach day dir
|
||||
}
|
||||
|
||||
Debug("Checking for Medium Scheme Events under $monitor_dir");
|
||||
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
||||
{
|
||||
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
|
||||
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." );
|
||||
|
@ -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,19 +104,23 @@ my @daemons = (
|
|||
'zmtelemetry.pl'
|
||||
);
|
||||
|
||||
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;
|
||||
|
@ -126,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);
|
||||
}
|
||||
}
|
||||
|
@ -137,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());
|
||||
|
@ -164,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
|
||||
|
@ -181,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++;
|
||||
|
@ -205,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);
|
||||
|
@ -214,9 +217,7 @@ while( my $line = <CLIENT> ) {
|
|||
chomp($line);
|
||||
print("$line\n");
|
||||
}
|
||||
# And we're done!
|
||||
close(CLIENT);
|
||||
#print( "Finished writing, bye\n" );
|
||||
|
||||
exit;
|
||||
|
||||
|
@ -233,54 +234,56 @@ 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;
|
||||
our $zm_terminate = 0;
|
||||
|
||||
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;
|
||||
$SIG{TERM} = \&shutdownAll;
|
||||
$SIG{ABRT} = \&shutdownAll;
|
||||
$SIG{INT} = \&shutdown_sig_handler;
|
||||
$SIG{TERM} = \&shutdown_sig_handler;
|
||||
$SIG{ABRT} = \&shutdown_sig_handler;
|
||||
$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;
|
||||
|
@ -289,17 +292,21 @@ 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 ) {
|
||||
while( !$zm_terminate ) {
|
||||
|
||||
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' );
|
||||
Debug("Connecting");
|
||||
while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) {
|
||||
Warning("Not connected to db ($dbh)".($dbh?" ping(".$dbh->ping().")":''). ($DBI::errstr?" errstr($DBI::errstr)":'').' Reconnecting');
|
||||
$dbh = zmDbConnect();
|
||||
}
|
||||
my @cpuload = CpuLoad();
|
||||
Debug("UPdating Server record @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());
|
||||
|
@ -307,52 +314,53 @@ sub run {
|
|||
}
|
||||
$secs_count += 1;
|
||||
}
|
||||
my $nfound = select( my $rout = $rin, undef, undef, $timeout );
|
||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||
Debug("Aftere select $nfound");
|
||||
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();
|
||||
# Breka out of while loop
|
||||
last;
|
||||
} 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 {
|
||||
|
@ -360,22 +368,26 @@ sub run {
|
|||
}
|
||||
} else {
|
||||
#print( "Select timed out\n" );
|
||||
restartPending();
|
||||
}
|
||||
}
|
||||
dPrint( ZoneMinder::Logger::INFO, 'Server exiting at '
|
||||
|
||||
Debug("restartPending");
|
||||
restartPending();
|
||||
Debug("check_for_processes_to_kill");
|
||||
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 );
|
||||
exit();
|
||||
shutdownAll();
|
||||
}
|
||||
|
||||
sub cPrint {
|
||||
|
@ -391,15 +403,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(@_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,61 +419,64 @@ 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: $!" );
|
||||
my $blockset = POSIX::SigSet->new(SIGCHLD);
|
||||
Debug("Blocking SIGCHLD");
|
||||
sigprocmask(SIG_BLOCK, $blockset, $sigset) or Fatal("Can't block SIGCHLD: $!");
|
||||
Debug("forking");
|
||||
if ( my $cpid = fork() ) {
|
||||
# This logReinit is required. Not sure why.
|
||||
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: $!");
|
||||
Debug("unblocking child");
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,8 +484,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
|
||||
|
@ -479,153 +494,167 @@ 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 {
|
||||
my ( $final, $process ) = @_;
|
||||
|
||||
my $sigset = POSIX::SigSet->new;
|
||||
my $blockset = POSIX::SigSet->new(SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
|
||||
|
||||
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"
|
||||
);
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||
return();
|
||||
}
|
||||
|
||||
my $pid = $process->{pid};
|
||||
if ( !$pid ) {
|
||||
dPrint(ZoneMinder::Logger::ERROR, "No process with command of '$command' is running\n");
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||
return();
|
||||
}
|
||||
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");
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\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 if ! $process->{term_sent_at};
|
||||
$process->{pending} = 0;
|
||||
$terminating_processes{$command} = $process;
|
||||
|
||||
kill('TERM', $pid);
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||
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"
|
||||
);
|
||||
kill( 'KILL', $$process{pid} );
|
||||
last;
|
||||
sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
|
||||
foreach my $command ( keys %terminating_processes ) {
|
||||
my $process = $cmd_hash{$command};
|
||||
if ( ! $process ) {
|
||||
Debug("No process found for $command");
|
||||
delete $terminating_processes{$command};
|
||||
next;
|
||||
}
|
||||
if ( ! $$process{pid} ) {
|
||||
Warning("Have no pid for $command.");
|
||||
delete $terminating_processes{$command};
|
||||
next;
|
||||
}
|
||||
my $now = time;
|
||||
Debug("Have process $command at pid $$process{pid} $now - $$process{term_sent_at} = " . ( $now - $$process{term_sent_at} ));
|
||||
if ( $$process{term_sent_at} and ( $now - $$process{term_sent_at} > 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});
|
||||
delete $terminating_processes{$command};
|
||||
}
|
||||
|
||||
# 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'");
|
||||
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 );
|
||||
return;
|
||||
} else {
|
||||
dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" );
|
||||
}
|
||||
}
|
||||
if ( !$process ) {
|
||||
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n");
|
||||
start($daemon, @args);
|
||||
return;
|
||||
}
|
||||
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 shutdown_sig_handler {
|
||||
$zm_terminate = 1;
|
||||
}
|
||||
|
||||
sub reaper {
|
||||
my $saved_status = $!;
|
||||
while ( (my $cpid = waitpid( -1, WNOHANG )) > 0 ) {
|
||||
|
||||
# Wait for a child to terminate
|
||||
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;
|
||||
|
@ -633,8 +662,8 @@ sub reaper {
|
|||
|
||||
my $out_str = "'$process->{command}' ";
|
||||
if ( $exit_signal ) {
|
||||
# 15 == TERM, 14 == ALARM
|
||||
if ( $exit_signal == 15 || $exit_signal == 14 ) {
|
||||
# TERM or ALRM
|
||||
$out_str .= 'exited';
|
||||
} else {
|
||||
$out_str .= 'crashed';
|
||||
|
@ -652,9 +681,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} ) {
|
||||
|
@ -672,49 +701,49 @@ sub reaper {
|
|||
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
|
||||
}
|
||||
}
|
||||
Debug("Delay for $$process{command} is now $$process{delay}");
|
||||
} else {
|
||||
delete $cmd_hash{$$process{command}};
|
||||
}
|
||||
}
|
||||
} # end while waitpid
|
||||
$SIG{CHLD} = \&reaper;
|
||||
$! = $saved_status;
|
||||
Debug("Leaving reaper");
|
||||
}
|
||||
|
||||
sub restartPending {
|
||||
# Restart any pending processes
|
||||
foreach my $process ( values( %cmd_hash ) ) {
|
||||
# Restart any pending processes, we list them first because cmd_hash may change in foreach
|
||||
my @processes = values %cmd_hash;
|
||||
foreach my $process ( @processes ) {
|
||||
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}});
|
||||
}
|
||||
}
|
||||
Debug("done restartPending");
|
||||
}
|
||||
|
||||
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 ( keys %terminating_processes ) {
|
||||
check_for_processes_to_kill();
|
||||
if ( %terminating_processes ) {
|
||||
Debug("Still " . %terminating_processes . ' to die. sleeping');
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
if ( 0 ) {
|
||||
killAll( 5 );
|
||||
}
|
||||
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 );
|
||||
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);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -722,18 +751,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -743,53 +772,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 -';
|
||||
|
@ -800,16 +830,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__
|
||||
|
|
|
@ -94,7 +94,7 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
|
|||
: ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
|
||||
;
|
||||
|
||||
logInit();
|
||||
logInit($filter_id?(id=>'zmfilter_'.$filter_id):());
|
||||
sub HupHandler {
|
||||
Info("Received HUP, reloading");
|
||||
&ZoneMinder::Logger::logHupHandler();
|
||||
|
@ -154,38 +154,38 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
|||
my $event_id = 0;
|
||||
|
||||
if ( ! EVENT_PATH ) {
|
||||
Error( "No event path defined. Config was $Config{ZM_DIR_EVENTS}\n" );
|
||||
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n");
|
||||
die;
|
||||
}
|
||||
|
||||
# In future, should not be neccessary wrt StorageAreas
|
||||
chdir( EVENT_PATH );
|
||||
|
||||
# SHould not be neccessary... but nice to get a local var. What if it fails?
|
||||
# Should not be neccessary... but nice to get a local var. What if it fails?
|
||||
my $dbh = zmDbConnect();
|
||||
|
||||
if ( $filter_name ) {
|
||||
Info( "Scanning for events using filter '$filter_name'\n" );
|
||||
Info("Scanning for events using filter '$filter_name'\n");
|
||||
} elsif ( $filter_id ) {
|
||||
Info( "Scanning for events using filter id '$filter_id'\n" );
|
||||
Info("Scanning for events using filter id '$filter_id'\n");
|
||||
} else {
|
||||
Info( "Scanning for events using all filters\n" );
|
||||
Info("Scanning for events using all filters\n");
|
||||
}
|
||||
|
||||
if ( ! ( $filter_name or $filter_id ) ) {
|
||||
Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...' );
|
||||
sleep( START_DELAY );
|
||||
Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...');
|
||||
sleep(START_DELAY);
|
||||
}
|
||||
|
||||
my @filters;
|
||||
my $last_action = 0;
|
||||
|
||||
while( ! $zm_terminate ) {
|
||||
while( !$zm_terminate ) {
|
||||
my $now = time;
|
||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
||||
Debug( "Reloading filters\n" );
|
||||
Debug("Reloading filters\n");
|
||||
$last_action = $now;
|
||||
@filters = getFilters( { Name=>$filter_name, Id=>$filter_id } );
|
||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||
}
|
||||
|
||||
foreach my $filter ( @filters ) {
|
||||
|
@ -195,16 +195,16 @@ while( ! $zm_terminate ) {
|
|||
my ( $id ) = $$filter{Id} =~ /(\d+)/;
|
||||
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
|
||||
|
||||
system( qq`$proc --filter "$$filter{Name}" &` );
|
||||
system(qq`$proc --filter "$$filter{Name}" &`);
|
||||
} else {
|
||||
checkFilter( $filter );
|
||||
checkFilter($filter);
|
||||
}
|
||||
}
|
||||
|
||||
last if $filter_name or $filter_id or $zm_terminate;
|
||||
|
||||
Debug( "Sleeping for $delay seconds\n" );
|
||||
sleep( $delay );
|
||||
Debug("Sleeping for $delay seconds\n");
|
||||
sleep($delay);
|
||||
}
|
||||
|
||||
sub getFilters {
|
||||
|
@ -232,17 +232,18 @@ sub getFilters {
|
|||
or UpdateDiskSpace = 1
|
||||
or AutoMove = 1
|
||||
) ORDER BY Name';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable to prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( @sql_values )
|
||||
or Fatal( "Unable to execute '$sql': ".$sth->errstr() );
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute(@sql_values)
|
||||
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
|
||||
FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
|
||||
my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter );
|
||||
Debug( "Found filter '$db_filter->{Name}'\n" );
|
||||
my $filter_sql = $filter->Sql();
|
||||
my $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter);
|
||||
Debug("Found filter '$db_filter->{Name}'");
|
||||
# The undef here is to make sure the Sql gets regenerated because the Filter object may be cached
|
||||
my $filter_sql = $filter->Sql(undef);
|
||||
|
||||
if ( ! $filter_sql ) {
|
||||
Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" );
|
||||
Error("Error parsing Sql. skipping filter '$db_filter->{Name}'");
|
||||
next FILTER;
|
||||
}
|
||||
push @filters, $filter;
|
||||
|
@ -251,7 +252,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
|
|||
if ( ! @filters ) {
|
||||
Warning("No filter found for $sql with values(@sql_values)");
|
||||
} else {
|
||||
Debug( "Got " . @filters . " filters" );
|
||||
Debug("Got " . @filters . " filters");
|
||||
}
|
||||
|
||||
return @filters;
|
||||
|
@ -282,65 +283,71 @@ sub checkFilter {
|
|||
|
||||
foreach my $event ( @Events ) {
|
||||
last if $zm_terminate;
|
||||
Debug( "Checking event $event->{Id}" );
|
||||
my $Event = new ZoneMinder::Event($$event{Id}, $event);
|
||||
|
||||
Debug("Checking event $event->{Id}");
|
||||
my $delete_ok = !undef;
|
||||
$dbh->ping();
|
||||
if ( $filter->{AutoArchive} ) {
|
||||
Info( "Archiving event $event->{Id}" );
|
||||
Info("Archiving event $event->{Id}");
|
||||
# Do it individually to avoid locking up the table for new events
|
||||
my $sql = 'UPDATE Events SET Archived = 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 Error( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
or Error("Unable to execute '$sql': ".$dbh->errstr());
|
||||
}
|
||||
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) {
|
||||
if ( !$event->{Videoed} ) {
|
||||
$delete_ok = undef if ( !generateVideo( $filter, $event ) );
|
||||
$delete_ok = undef if !generateVideo($filter, $event);
|
||||
}
|
||||
}
|
||||
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} ) {
|
||||
if ( !$event->{Messaged} ) {
|
||||
$delete_ok = undef if ( !sendMessage( $filter, $event ) );
|
||||
$delete_ok = undef if !sendMessage($filter, $event);
|
||||
}
|
||||
}
|
||||
if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) {
|
||||
if ( !$event->{Uploaded} ) {
|
||||
$delete_ok = undef if ( !uploadArchFile( $filter, $event ) );
|
||||
$delete_ok = undef if !uploadArchFile($filter, $event);
|
||||
}
|
||||
}
|
||||
if ( $filter->{AutoExecute} ) {
|
||||
if ( !$event->{Executed} ) {
|
||||
$delete_ok = undef if ( !executeCommand( $filter, $event ) );
|
||||
$delete_ok = undef if !executeCommand($filter, $event);
|
||||
}
|
||||
}
|
||||
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" );
|
||||
Error("Unable toto delete event $event->{Id} as previous operations failed");
|
||||
}
|
||||
} # end if AutoDelete
|
||||
|
||||
if ( $filter->{AutoMove} ) {
|
||||
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
|
||||
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );
|
||||
$_ = $Event->MoveTo( $NewStorage );
|
||||
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();
|
||||
|
@ -357,7 +364,7 @@ sub generateVideo {
|
|||
my $scale = $event->{DefaultScale}/100;
|
||||
my $format;
|
||||
|
||||
my @ffmpeg_formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} );
|
||||
my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS});
|
||||
my $default_video_format;
|
||||
my $default_phone_format;
|
||||
foreach my $ffmpeg_format( @ffmpeg_formats ) {
|
||||
|
@ -391,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() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($event->{Id})
|
||||
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
|
||||
if ( wantarray() ) {
|
||||
return( $format, $output );
|
||||
}
|
||||
}
|
||||
return( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Returns an image absolute path for given event and frame
|
||||
|
@ -417,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
|
||||
|
@ -431,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;
|
||||
}
|
||||
}
|
||||
|
@ -445,12 +463,12 @@ sub uploadArchFile {
|
|||
my $event = shift;
|
||||
|
||||
if ( ! $Config{ZM_UPLOAD_HOST} ) {
|
||||
Error( 'Cannot upload archive as no upload host defined' );
|
||||
Error('Cannot upload archive as no upload host defined');
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
my $archFile = $event->{MonitorName}.'-'.$event->{Id};
|
||||
my $archImagePath = getEventPath( $event )
|
||||
my $archImagePath = getEventPath($event)
|
||||
.'/'
|
||||
.(
|
||||
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
|
||||
|
@ -467,14 +485,14 @@ sub uploadArchFile {
|
|||
$archFile .= '.zip';
|
||||
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
||||
my $zip = Archive::Zip->new();
|
||||
Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" );
|
||||
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
|
||||
|
||||
my $status = &AZ_OK;
|
||||
foreach my $imageFile ( @archImageFiles ) {
|
||||
Debug( "Adding $imageFile\n" );
|
||||
my $member = $zip->addFile( $imageFile );
|
||||
Debug("Adding $imageFile\n");
|
||||
my $member = $zip->addFile($imageFile);
|
||||
if ( !$member ) {
|
||||
Error( "Unable toto add image file $imageFile to zip archive $archLocPath" );
|
||||
Error("Unable toto add image file $imageFile to zip archive $archLocPath");
|
||||
$archError = 1;
|
||||
last;
|
||||
}
|
||||
|
@ -487,10 +505,10 @@ sub uploadArchFile {
|
|||
$status = $zip->writeToFileNamed( $archLocPath );
|
||||
|
||||
if ( $archError = ($status != &AZ_OK) ) {
|
||||
Error( "Zip error: $status\n " );
|
||||
Error("Zip error: $status");
|
||||
}
|
||||
} else {
|
||||
Error( "Error adding images to zip archive $archLocPath, not writing" );
|
||||
Error("Error adding images to zip archive $archLocPath, not writing");
|
||||
}
|
||||
} elsif ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'tar' ) {
|
||||
if ( $Config{ZM_UPLOAD_ARCH_COMPRESS} ) {
|
||||
|
@ -499,7 +517,7 @@ sub uploadArchFile {
|
|||
$archFile .= '.tar';
|
||||
}
|
||||
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
||||
Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" );
|
||||
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
|
||||
|
||||
if ( $archError = !Archive::Tar->create_archive(
|
||||
$archLocPath,
|
||||
|
@ -507,7 +525,7 @@ sub uploadArchFile {
|
|||
@archImageFiles
|
||||
)
|
||||
) {
|
||||
Error( 'Tar error: '.Archive::Tar->error()."\n " );
|
||||
Error('Tar error: '.Archive::Tar->error());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,7 +533,7 @@ sub uploadArchFile {
|
|||
return( 0 );
|
||||
} else {
|
||||
if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) {
|
||||
Info( 'Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP\n" );
|
||||
Info('Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP");
|
||||
my $ftp = Net::FTP->new(
|
||||
$Config{ZM_UPLOAD_HOST},
|
||||
Timeout=>$Config{ZM_UPLOAD_TIMEOUT},
|
||||
|
@ -523,63 +541,61 @@ sub uploadArchFile {
|
|||
Debug=>$Config{ZM_UPLOAD_DEBUG}
|
||||
);
|
||||
if ( !$ftp ) {
|
||||
Error( "Unable tocreate FTP connection: $@" );
|
||||
return( 0 );
|
||||
Error("Unable tocreate FTP connection: $@");
|
||||
return 0;
|
||||
}
|
||||
$ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} )
|
||||
or Error( "FTP - Unable tologin" );
|
||||
$ftp->login($Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS})
|
||||
or Error("FTP - Unable tologin");
|
||||
$ftp->binary()
|
||||
or Error( "FTP - Unable togo binary" );
|
||||
$ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} )
|
||||
or Error( "FTP - Unable tocwd" )
|
||||
or Error("FTP - Unable togo binary");
|
||||
$ftp->cwd($Config{ZM_UPLOAD_REM_DIR})
|
||||
or Error("FTP - Unable tocwd")
|
||||
if ( $Config{ZM_UPLOAD_REM_DIR} );
|
||||
$ftp->put( $archLocPath )
|
||||
or Error( "FTP - Unable toupload '$archLocPath'" );
|
||||
or Error("FTP - Unable toupload '$archLocPath'");
|
||||
$ftp->quit()
|
||||
or Error( "FTP - Unable toquit" );
|
||||
or Error("FTP - Unable toquit");
|
||||
} else {
|
||||
my $host = $Config{ZM_UPLOAD_HOST};
|
||||
$host .= ':'.$Config{ZM_UPLOAD_PORT}
|
||||
if $Config{ZM_UPLOAD_PORT};
|
||||
Info( 'Uploading to '.$host." using SFTP\n" );
|
||||
my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} );
|
||||
$sftpOptions{password} = $Config{ZM_UPLOAD_PASS}
|
||||
if $Config{ZM_UPLOAD_PASS};
|
||||
$sftpOptions{port} = $Config{ZM_UPLOAD_PORT}
|
||||
if $Config{ZM_UPLOAD_PORT};
|
||||
$sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT}
|
||||
if $Config{ZM_UPLOAD_TIMEOUT};
|
||||
$host .= ':'.$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT};
|
||||
Info('Uploading to '.$host." using SFTP\n");
|
||||
my %sftpOptions = (
|
||||
host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER}
|
||||
($Config{ZM_UPLOAD_PASS} ? (password=>$Config{ZM_UPLOAD_PASS}) : ()),
|
||||
($Config{ZM_UPLOAD_PORT} ? (port=>$Config{ZM_UPLOAD_PORT}) : ()),
|
||||
($Config{ZM_UPLOAD_TIMEOUT} ? (timeout=>$Config{ZM_UPLOAD_TIMEOUT}) : ()),
|
||||
);
|
||||
my @more_ssh_args;
|
||||
push @more_ssh_args, '-o'=>'StrictHostKeyChecking=no'
|
||||
if ! $Config{ZM_UPLOAD_STRICT};
|
||||
push @more_ssh_args, '-v'
|
||||
if $Config{ZM_UPLOAD_DEBUG};
|
||||
$sftpOptions{more} = [@more_ssh_args];
|
||||
my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions );
|
||||
my $sftp = Net::SFTP::Foreign->new($Config{ZM_UPLOAD_HOST}, %sftpOptions);
|
||||
if ( $sftp->error ) {
|
||||
Error( "Unable tocreate SFTP connection: ".$sftp->error );
|
||||
return( 0 );
|
||||
Error("Unable tocreate SFTP connection: ".$sftp->error);
|
||||
return 0;
|
||||
}
|
||||
$sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} )
|
||||
or Error( "SFTP - Unable tosetcwd: ".$sftp->error )
|
||||
$sftp->setcwd($Config{ZM_UPLOAD_REM_DIR})
|
||||
or Error("SFTP - Unable to setcwd: ".$sftp->error)
|
||||
if $Config{ZM_UPLOAD_REM_DIR};
|
||||
$sftp->put( $archLocPath, $archFile )
|
||||
or Error( "SFTP - Unable toupload '$archLocPath': ".$sftp->error );
|
||||
$sftp->put($archLocPath, $archFile)
|
||||
or Error("SFTP - Unable to upload '$archLocPath': ".$sftp->error);
|
||||
}
|
||||
unlink( $archLocPath );
|
||||
unlink($archLocPath);
|
||||
my $sql = 'UPDATE Events SET Uploaded = 1 WHERE Id = ?';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($event->{Id})
|
||||
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
|
||||
}
|
||||
return( 1 );
|
||||
}
|
||||
return 1;
|
||||
} # end 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
|
||||
|
@ -587,35 +603,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)%/;
|
||||
|
@ -623,14 +611,14 @@ sub substituteTags {
|
|||
my $max_alarm_frame;
|
||||
my $max_alarm_score = 0;
|
||||
if ( $need_images ) {
|
||||
my $sql = "SELECT * FROM Frames
|
||||
my $sql = q`SELECT * FROM Frames
|
||||
WHERE EventId = ? AND Type = 'Alarm'
|
||||
ORDER BY FrameId"
|
||||
;
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
ORDER BY FrameId`;
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($Event->{Id})
|
||||
or Fatal( "Unable toexecute '$sql': ".$dbh->errstr());
|
||||
my $rows = 0;
|
||||
while( my $frame = $sth->fetchrow_hashref() ) {
|
||||
if ( !$first_alarm_frame ) {
|
||||
$first_alarm_frame = $frame;
|
||||
|
@ -639,72 +627,80 @@ 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 ) {
|
||||
# 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, '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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,49 +708,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');
|
||||
|
||||
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'");
|
||||
|
||||
eval {
|
||||
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
|
||||
|
@ -772,7 +768,7 @@ sub sendEmail {
|
|||
);
|
||||
### Add the attachments
|
||||
foreach my $attachment ( @attachments ) {
|
||||
Info( "Attaching '$attachment->{path}\n" );
|
||||
Info( "Attaching '$attachment->{path}'" );
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
Type => $attachment->{type},
|
||||
|
@ -784,20 +780,20 @@ sub sendEmail {
|
|||
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
|
||||
if ( !$ssmtp_location ) {
|
||||
if ( logDebugging() ) {
|
||||
Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" );
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
}
|
||||
$ssmtp_location = qx('which ssmtp');
|
||||
}
|
||||
if ( !$ssmtp_location ) {
|
||||
Debug( "Unable tofind ssmtp, trying MIME::Lite->send" );
|
||||
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
|
||||
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||
$mail->send();
|
||||
} else {
|
||||
### Send using SSMTP
|
||||
$mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} );
|
||||
$mail->send('sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS});
|
||||
}
|
||||
} else {
|
||||
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||
$mail->send();
|
||||
}
|
||||
} else {
|
||||
|
@ -810,29 +806,29 @@ sub sendEmail {
|
|||
);
|
||||
|
||||
foreach my $attachment ( @attachments ) {
|
||||
Info( "Attaching '$attachment->{path}\n" );
|
||||
Info("Attaching '$attachment->{path}'");
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
Type => $attachment->{type},
|
||||
Encoding => 'base64'
|
||||
);
|
||||
}
|
||||
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} );
|
||||
$mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL});
|
||||
}
|
||||
};
|
||||
if ( $@ ) {
|
||||
Error( "Unable tosend email: $@" );
|
||||
return( 0 );
|
||||
Error("Unable tosend email: $@");
|
||||
return 0;
|
||||
} else {
|
||||
Info( "Notification email sent\n" );
|
||||
Info('Notification email sent');
|
||||
}
|
||||
my $sql = 'update Events set Emailed = 1 where Id = ?';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
my $sql = 'UPDATE Events SET Emailed = 1 WHERE Id = ?';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($Event->{Id})
|
||||
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
|
||||
|
||||
return( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub sendMessage {
|
||||
|
@ -840,28 +836,28 @@ sub sendMessage {
|
|||
my $event = shift;
|
||||
|
||||
if ( ! $Config{ZM_FROM_EMAIL} ) {
|
||||
Error( "No 'from' email address defined, not sending message" );
|
||||
return( 0 );
|
||||
Error('No from email address defined, not sending message');
|
||||
return 0;
|
||||
}
|
||||
if ( ! $Config{ZM_MESSAGE_ADDRESS} ) {
|
||||
Error( 'No message address defined, not sending message' );
|
||||
return( 0 );
|
||||
Error('No message address defined, not sending message');
|
||||
return 0;
|
||||
}
|
||||
|
||||
Info( "Creating notification message\n" );
|
||||
Info('Creating notification message');
|
||||
|
||||
my $subject = substituteTags( $Config{ZM_MESSAGE_SUBJECT}, $filter, $event );
|
||||
return( 0 ) if ( !$subject );
|
||||
my $subject = substituteTags($Config{ZM_MESSAGE_SUBJECT}, $filter, $event);
|
||||
return 0 if !$subject;
|
||||
my @attachments;
|
||||
my $body = substituteTags( $Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments );
|
||||
return( 0 ) if ( !$body );
|
||||
my $body = substituteTags($Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments);
|
||||
return 0 if !$body;
|
||||
|
||||
Info( "Sending notification message '$subject'\n" );
|
||||
Info("Sending notification message '$subject'");
|
||||
|
||||
eval {
|
||||
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
|
||||
### Create the multipart container
|
||||
my $mail = MIME::Lite->new (
|
||||
my $mail = MIME::Lite->new(
|
||||
From => $Config{ZM_FROM_EMAIL},
|
||||
To => $Config{ZM_MESSAGE_ADDRESS},
|
||||
Subject => $subject,
|
||||
|
@ -874,7 +870,7 @@ sub sendMessage {
|
|||
);
|
||||
### Add the attachments
|
||||
foreach my $attachment ( @attachments ) {
|
||||
Info( "Attaching '$attachment->{path}\n" );
|
||||
Info("Attaching '$attachment->{path}");
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
Type => $attachment->{type},
|
||||
|
@ -886,20 +882,20 @@ sub sendMessage {
|
|||
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
|
||||
if ( !$ssmtp_location ) {
|
||||
if ( logDebugging() ) {
|
||||
Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" );
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
}
|
||||
$ssmtp_location = qx('which ssmtp');
|
||||
}
|
||||
if ( !$ssmtp_location ) {
|
||||
Debug( 'Unable tofind ssmtp, trying MIME::Lite->send' );
|
||||
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
|
||||
MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||
$mail->send();
|
||||
} else {
|
||||
### Send using SSMTP
|
||||
$mail->send( 'sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS} );
|
||||
$mail->send('sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS});
|
||||
}
|
||||
} else {
|
||||
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||
$mail->send();
|
||||
}
|
||||
} else {
|
||||
|
@ -912,59 +908,60 @@ sub sendMessage {
|
|||
);
|
||||
|
||||
foreach my $attachment ( @attachments ) {
|
||||
Info( "Attaching '$attachment->{path}\n" );
|
||||
Info("Attaching '$attachment->{path}'");
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
Type => $attachment->{type},
|
||||
Encoding => 'base64'
|
||||
);
|
||||
}
|
||||
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST},
|
||||
$mail->smtpsend(
|
||||
Host => $Config{ZM_EMAIL_HOST},
|
||||
MailFrom => $Config{ZM_FROM_EMAIL}
|
||||
);
|
||||
}
|
||||
};
|
||||
if ( $@ ) {
|
||||
Error( "Unable tosend email: $@" );
|
||||
return( 0 );
|
||||
Error("Unable tosend email: $@");
|
||||
return 0;
|
||||
} else {
|
||||
Info( "Notification message sent\n" );
|
||||
Info('Notification message sent');
|
||||
}
|
||||
my $sql = 'update Events set Messaged = 1 where Id = ?';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
my $sql = 'UPDATE Events SET Messaged = 1 WHERE Id = ?';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($event->{Id})
|
||||
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
|
||||
|
||||
return( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub executeCommand {
|
||||
my $filter = shift;
|
||||
my $event = shift;
|
||||
|
||||
my $event_path = getEventPath( $event );
|
||||
my $event_path = getEventPath($event);
|
||||
|
||||
my $command = $filter->{AutoExecuteCmd};
|
||||
$command .= " $event_path";
|
||||
$command = substituteTags( $command, $filter, $event );
|
||||
$command = substituteTags($command, $filter, $event);
|
||||
|
||||
Info( "Executing '$command'\n" );
|
||||
Info("Executing '$command'");
|
||||
my $output = qx($command);
|
||||
my $status = $? >> 8;
|
||||
if ( $status || logDebugging() ) {
|
||||
chomp( $output );
|
||||
Debug( "Output: $output\n" );
|
||||
chomp($output);
|
||||
Debug("Output: $output");
|
||||
}
|
||||
if ( $status ) {
|
||||
Error( "Command '$command' exited with status: $status\n" );
|
||||
return( 0 );
|
||||
Error("Command '$command' exited with status: $status");
|
||||
return 0;
|
||||
} else {
|
||||
my $sql = 'update Events set Executed = 1 where Id = ?';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
my $sql = 'UPDATE Events SET Executed = 1 WHERE Id = ?';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute( $event->{Id} )
|
||||
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
|
||||
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
|
||||
}
|
||||
return( 1 );
|
||||
}
|
||||
|
|
|
@ -205,12 +205,13 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
$sql = 'SELECT * FROM Monitors';
|
||||
}
|
||||
|
||||
{
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
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 {
|
||||
|
@ -226,9 +227,24 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
}
|
||||
}
|
||||
$sth->finish();
|
||||
|
||||
}
|
||||
{
|
||||
my $sql = 'SELECT Id FROM Filters WHERE Background=1';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute()
|
||||
or Fatal( "Can't execute: ".$sth->errstr() );
|
||||
if ( $sth->rows ) {
|
||||
while( my $filter = $sth->fetchrow_hashref() ) {
|
||||
# This is now started unconditionally
|
||||
runCommand('zmdc.pl start zmfilter.pl');
|
||||
runCommand("zmdc.pl start zmfilter.pl --filter_id=$$filter{Id}");
|
||||
}
|
||||
} else {
|
||||
runCommand('zmdc.pl start zmfilter.pl');
|
||||
}
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
if ( $Config{ZM_RUN_AUDIT} ) {
|
||||
if ( $Server and exists $$Server{'zmaudit'} and ! $$Server{'zmaudit'} ) {
|
||||
Debug("Not running zmaudit.pl because it is turned off for this server.");
|
||||
|
@ -253,6 +269,9 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
if ( $Config{ZM_TELEMETRY_DATA} ) {
|
||||
runCommand('zmdc.pl start zmtelemetry.pl');
|
||||
}
|
||||
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
|
||||
runCommand('zmdc.pl start zmeventnotification.pl');
|
||||
}
|
||||
if ( $Server and exists $$Server{'zmstats'} and ! $$Server{'zmstats'} ) {
|
||||
Debug("Not running zmstats.pl because it is turned off for this server.");
|
||||
} else {
|
||||
|
|
|
@ -53,67 +53,70 @@ GetOptions(
|
|||
version => \$version
|
||||
);
|
||||
if ( $version ) {
|
||||
print( ZoneMinder::Base::ZM_VERSION . "\n");
|
||||
exit(0);
|
||||
print( ZoneMinder::Base::ZM_VERSION . "\n");
|
||||
exit(0);
|
||||
}
|
||||
if ( $help ) {
|
||||
pod2usage(-exitstatus => -1);
|
||||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
if ( ! defined $interval ) {
|
||||
$interval = eval($Config{ZM_TELEMETRY_INTERVAL});
|
||||
}
|
||||
|
||||
if ( $Config{ZM_TELEMETRY_DATA} or $force ) {
|
||||
print "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n";
|
||||
if ( !($Config{ZM_TELEMETRY_DATA} or $force) ) {
|
||||
print "ZoneMinder Telemetry Agent not enabled. Exiting.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
print 'ZoneMinder Telemetry Agent starting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n";
|
||||
|
||||
my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
|
||||
my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
|
||||
|
||||
while( 1 ) {
|
||||
my $now = time();
|
||||
my $since_last_check = $now-$lastCheck;
|
||||
Debug(" Last Check time (now($now) - lastCheck($lastCheck)) = $since_last_check > interval($interval) or force($force)");
|
||||
if ( $since_last_check < 0 ) {
|
||||
Warning( "Seconds since last check is negative! Which means that lastCheck is in the future!" );
|
||||
next;
|
||||
}
|
||||
if ( ( ($now-$lastCheck) > $interval ) or $force ) {
|
||||
print "Collecting data to send to ZoneMinder Telemetry server.\n";
|
||||
my $dbh = zmDbConnect();
|
||||
while( 1 ) {
|
||||
my $now = time();
|
||||
my $since_last_check = $now-$lastCheck;
|
||||
Debug(" Last Check time (now($now) - lastCheck($lastCheck)) = $since_last_check > interval($interval) or force($force)");
|
||||
if ( $since_last_check < 0 ) {
|
||||
Warning( 'Seconds since last check is negative! Which means that lastCheck is in the future!' );
|
||||
next;
|
||||
}
|
||||
if ( ( ($since_last_check) > $interval ) or $force ) {
|
||||
print "Collecting data to send to ZoneMinder Telemetry server.\n";
|
||||
my $dbh = zmDbConnect();
|
||||
# Build the telemetry hash
|
||||
# We should keep *BSD systems in mind when calling system commands
|
||||
my %telemetry;
|
||||
$telemetry{uuid} = getUUID($dbh);
|
||||
$telemetry{ip} = getIP();
|
||||
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
|
||||
$telemetry{monitor_count} = countQuery($dbh,'Monitors');
|
||||
$telemetry{event_count} = countQuery($dbh,'Events');
|
||||
$telemetry{architecture} = runSysCmd('uname -p');
|
||||
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
|
||||
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
||||
$telemetry{system_memory} = totalmem();
|
||||
$telemetry{processor_count} = cpu_count();
|
||||
$telemetry{monitors} = getMonitorRef($dbh);
|
||||
my %telemetry;
|
||||
$telemetry{uuid} = getUUID($dbh);
|
||||
$telemetry{ip} = getIP();
|
||||
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
|
||||
$telemetry{monitor_count} = countQuery($dbh,'Monitors');
|
||||
$telemetry{event_count} = countQuery($dbh,'Events');
|
||||
$telemetry{architecture} = runSysCmd('uname -p');
|
||||
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
|
||||
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
||||
$telemetry{system_memory} = totalmem();
|
||||
$telemetry{processor_count} = cpu_count();
|
||||
$telemetry{monitors} = getMonitorRef($dbh);
|
||||
|
||||
Info( 'Sending data to ZoneMinder Telemetry server.' );
|
||||
Info('Sending data to ZoneMinder Telemetry server.');
|
||||
|
||||
my $result = jsonEncode( \%telemetry );
|
||||
my $result = jsonEncode(\%telemetry);
|
||||
|
||||
if ( sendData($result) ) {
|
||||
|
||||
my $sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_LAST_UPLOAD'`;
|
||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $now ) or die( "Can't execute: ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
$Config{ZM_TELEMETRY_LAST_UPLOAD} = $now;
|
||||
}
|
||||
zmDbDisconnect();
|
||||
} elsif ( -t STDIN ) {
|
||||
print "Update agent sleeping for 1 hour because ($now-$lastCheck=$since_last_check > $interval\n";
|
||||
}
|
||||
sleep( 3600 );
|
||||
}
|
||||
print 'Update agent exiting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n";
|
||||
}
|
||||
if ( sendData($result) ) {
|
||||
my $sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_LAST_UPLOAD'`;
|
||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute($now) or die( "Can't execute: ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
$Config{ZM_TELEMETRY_LAST_UPLOAD} = $now;
|
||||
}
|
||||
zmDbDisconnect();
|
||||
} elsif ( -t STDIN ) {
|
||||
print "ZoneMinder Telemetry Agent sleeping for $interval seconds because ($now-$lastCheck=$since_last_check > $interval\n";
|
||||
}
|
||||
$lastCheck = $now;
|
||||
sleep($interval);
|
||||
} # end while
|
||||
print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', localtime())."\n";
|
||||
|
||||
###############
|
||||
# SUBROUTINES #
|
||||
|
@ -178,7 +181,7 @@ sub sendData {
|
|||
# Retrieves the UUID from the database. Creates a new UUID if one does not exist.
|
||||
sub getUUID {
|
||||
my $dbh = shift;
|
||||
my $uuid= "";
|
||||
my $uuid= '';
|
||||
|
||||
# Verify the current UUID is valid and not nil
|
||||
if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne '00000000-0000-0000-0000-000000000000' )) {
|
||||
|
|
|
@ -197,8 +197,6 @@ my $sql = " SELECT (SELECT max(Delta) FROM Frames WHERE EventId=Events.Id)-(SELE
|
|||
Events.*,
|
||||
unix_timestamp(Events.StartTime) as Time,
|
||||
M.Name as MonitorName,
|
||||
M.Width as MonitorWidth,
|
||||
M.Height as MonitorHeight,
|
||||
M.Palette
|
||||
FROM Events
|
||||
INNER JOIN Monitors as M on Events.MonitorId = M.Id
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
||||
|
||||
# Group together all the source files that are used by all the binaries (zmc, zmu, zms etc)
|
||||
set(ZM_BIN_SRC_FILES zm_analysis_thread.cpp zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_ffmpeg_input.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
||||
set(ZM_BIN_SRC_FILES
|
||||
zm_analysis_thread.cpp
|
||||
zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_ffmpeg_input.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
||||
|
||||
# A fix for cmake recompiling the source files for every target.
|
||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||
|
|
|
@ -1,53 +1,68 @@
|
|||
//
|
||||
// ZoneMinder Camera Class Implementation, $Date$, $Revision$
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
//
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_camera.h"
|
||||
|
||||
Camera::Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||
monitor_id( p_monitor_id ),
|
||||
type( p_type ),
|
||||
width( p_width),
|
||||
height( p_height ),
|
||||
colours( p_colours ),
|
||||
subpixelorder( p_subpixelorder ),
|
||||
brightness( p_brightness ),
|
||||
hue( p_hue ),
|
||||
colour( p_colour ),
|
||||
contrast( p_contrast ),
|
||||
capture( p_capture ),
|
||||
record_audio( p_record_audio ),
|
||||
Camera::Camera(
|
||||
unsigned int p_monitor_id,
|
||||
SourceType p_type,
|
||||
unsigned int p_width,
|
||||
unsigned int p_height,
|
||||
int p_colours,
|
||||
int p_subpixelorder,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio
|
||||
) :
|
||||
monitor_id(p_monitor_id),
|
||||
type(p_type),
|
||||
width(p_width),
|
||||
height(p_height),
|
||||
colours(p_colours),
|
||||
subpixelorder(p_subpixelorder),
|
||||
brightness(p_brightness),
|
||||
hue(p_hue),
|
||||
colour(p_colour),
|
||||
contrast(p_contrast),
|
||||
capture(p_capture),
|
||||
record_audio(p_record_audio),
|
||||
mVideoStreamId(-1),
|
||||
mAudioStreamId(-1),
|
||||
mVideoCodecContext(NULL),
|
||||
mAudioCodecContext(NULL),
|
||||
video_stream(NULL)
|
||||
video_stream(NULL),
|
||||
bytes(0)
|
||||
{
|
||||
pixels = width * height;
|
||||
imagesize = pixels * colours;
|
||||
|
||||
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",monitor_id,width,height,colours,subpixelorder,capture);
|
||||
|
||||
|
||||
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",
|
||||
monitor_id,width,height,colours,subpixelorder,capture);
|
||||
|
||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
||||
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 64) != 0) {
|
||||
if ( (colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 64) != 0 ) {
|
||||
Fatal("Image size is not multiples of 64");
|
||||
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 64) != 0 || (imagesize % 12) != 0)) {
|
||||
} else if ( colours == ZM_COLOUR_RGB24 && ((imagesize % 64) != 0 || (imagesize % 12) != 0) ) {
|
||||
Fatal("Image size is not multiples of 12 and 64");
|
||||
}
|
||||
monitor = NULL;
|
||||
|
@ -57,14 +72,12 @@ Camera::~Camera() {
|
|||
}
|
||||
|
||||
Monitor *Camera::getMonitor() {
|
||||
if ( ! monitor ) {
|
||||
Warning("Loading monitor");
|
||||
monitor = Monitor::Load( monitor_id, false, Monitor::QUERY );
|
||||
}
|
||||
if ( ! monitor )
|
||||
monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::setMonitor( Monitor *p_monitor ) {
|
||||
void Camera::setMonitor(Monitor *p_monitor) {
|
||||
monitor = p_monitor;
|
||||
monitor_id = monitor->Id();
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ protected:
|
|||
int contrast;
|
||||
bool capture;
|
||||
bool record_audio;
|
||||
unsigned int bytes;
|
||||
|
||||
|
||||
int mVideoStreamId;
|
||||
int mAudioStreamId;
|
||||
|
@ -89,10 +91,11 @@ public:
|
|||
bool IscURL() const { return( type == CURL_SRC ); }
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( colours ); }
|
||||
unsigned int Colours() const { return colours; }
|
||||
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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue