Merge branch 'storageareas' of github.com:connortechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2018-04-02 10:43:07 -07:00
commit 793f630ee0
923 changed files with 42844 additions and 18554 deletions

View File

@ -23,7 +23,15 @@ addons:
- curl
- sshfs
- sed
- binfmt-support
- qemu
- qemu-user-static
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
@ -34,6 +42,8 @@ env:
- 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:

View File

@ -58,6 +58,7 @@ 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)
# 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")
@ -482,24 +483,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")

View File

@ -11,3 +11,6 @@ install(FILES ${dbfileslist} 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")

262
db/triggers.sql Normal file
View File

@ -0,0 +1,262 @@
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 ;
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 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 ;
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;
//
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 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;
ELSEIF ( 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(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(MonthEvents,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;
//
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 ;

View File

@ -229,37 +229,6 @@ 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)-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(DayEventDiskSpace,0)-OLD.DiskSpace WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET HourEventDiskSpace=COALESCE(DayEventDiskSpace,0)+NEW.DiskSpace WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET HourEventDiskSpace=COALESCE(DayEventDiskSpace,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,
@ -271,37 +240,6 @@ 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)-OLD.DiskSpace WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+NEW.DiskSpace 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,
@ -313,37 +251,6 @@ 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)-OLD.DiskSpace WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+NEW.DiskSpace 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,
@ -355,38 +262,6 @@ 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)-OLD.DiskSpace WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+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` (
@ -397,132 +272,6 @@ CREATE TABLE `Events_Archived` (
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 = NEW.DiskSpace - OLD.DiskSpace;
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff);
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
END IF;
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
END IF;
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
IF ( NEW.Archived != OLD.Archived ) THEN
IF ( NEW.Archived ) THEN
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END IF;
END IF;
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
END IF;
END;
//
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 = ArchivedEvents - 1,
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
TotalEvents = TotalEvents - 1,
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = TotalEvents-1,
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
--
-- Table structure for table `Filters`
--
@ -754,18 +503,18 @@ CREATE TABLE `Monitors` (
`WebColour` varchar(32) NOT NULL default 'red',
`Exif` tinyint(1) unsigned NOT NULL default '0',
`Sequence` smallint(5) unsigned default NULL,
`TotalEvents` int(10) unsigned,
`TotalEventDiskSpace` bigint unsigned,
`HourEvents` int(10) unsigned,
`HourEventDiskSpace` bigint unsigned,
`DayEvents` int(10) unsigned,
`DayEventDiskSpace` bigint unsigned,
`WeekEvents` int(10) unsigned,
`WeekEventDiskSpace` bigint unsigned,
`MonthEvents` int(10) unsigned,
`MonthEventDiskSpace` bigint unsigned,
`ArchivedEvents` int(10) unsigned,
`ArchivedEventDiskSpace` bigint unsigned,
`TotalEvents` int(10) default NULL,
`TotalEventDiskSpace` bigint default NULL,
`HourEvents` int(10) default NULL,
`HourEventDiskSpace` bigint default NULL,
`DayEvents` int(10) default NULL,
`DayEventDiskSpace` bigint default NULL,
`WeekEvents` int(10) default NULL,
`WeekEventDiskSpace` bigint default NULL,
`MonthEvents` int(10) default NULL,
`MonthEventDiskSpace` bigint default NULL,
`ArchivedEvents` int(10) default NULL,
`ArchivedEventDiskSpace` bigint default NULL,
`ZoneCount` TINYINT NOT NULL DEFAULT 0,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@ -947,30 +696,13 @@ 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,
@ -1039,6 +771,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
@ -1137,7 +873,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,
@ -1153,10 +889,11 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('3 Wide', '{ "default":{
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"24.5%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @ZM_PATH_DATA@/db/triggers.sql
--
-- Apply the initial configuration
--
-- This section is autogenerated by zmconfgen.pl
-- Do not edit this file as any changes will be overwritten
--

View File

@ -3,6 +3,18 @@ alter table Events DROP Primary key;
alter table Events Add Primary key(Id);
alter table Events modify Id int(10) unsigned auto_increment;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'
AND column_name = 'DiskSpace'
) > 0,
"SELECT 'Column DiskSpace already exists in Storage'",
"ALTER TABLE Storage ADD `DiskSpace` BIGINT default null AFTER `Type`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'

View File

@ -145,4 +145,4 @@ WeekEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.I
MonthEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB( NOW(), INTERVAL 1 month)),
MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 month) AND DiskSpace IS NOT NULL),
ArchivedEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1),
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1 AND DiskSpace IS NOT NULL)
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1 AND DiskSpace IS NOT NULL);

276
db/zm_update-1.31.38.sql Normal file
View File

@ -0,0 +1,276 @@
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 ;
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 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 ;
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;
//
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 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;
ELSEIF ( 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(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(MonthEvents,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;
//
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 ;
UPDATE Monitors SET
TotalEvents=(SELECT COUNT(*) FROM Events WHERE MonitorId=Monitors.Id),
TotalEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
HourEvents=(SELECT COUNT(*) FROM Events_Hour WHERE MonitorId=Monitors.Id),
HourEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Hour WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
DayEvents=(SELECT COUNT(*) FROM Events_Day WHERE MonitorId=Monitors.Id),
DayEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Day WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
WeekEvents=(SELECT COUNT(Id) FROM Events_Week WHERE MonitorId=Monitors.Id),
WeekEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Week WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
MonthEvents=(SELECT COUNT(Id) FROM Events_Month WHERE MonitorId=Monitors.Id),
MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Month WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
ArchivedEvents=(SELECT COUNT(Id) FROM Events_Archived WHERE MonitorId=Monitors.Id),
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Archived WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL);

275
db/zm_update-1.31.39.sql Normal file
View File

@ -0,0 +1,275 @@
ALTER TABLE `Monitors` MODIFY `HourEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `HourEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `DayEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `DayEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `WeekEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `WeekEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `MonthEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `MonthEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `ArchivedEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `ArchivedEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `TotalEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `TotalEventDiskSpace` BIGINT DEFAULT NULL;
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 ;
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 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 ;
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;
//
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 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;
ELSEIF ( 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(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(MonthEvents,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;
//
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 ;

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

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = '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;

View File

@ -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

View File

@ -83,6 +83,10 @@ BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel
# Required for mp4 container support
BuildRequires: libmp4v2-devel
BuildRequires: x264-devel
%{?with_nginx:Requires: nginx}
%{?with_nginx:Requires: fcgiwrap}
%{?with_nginx:Requires: php-fpm}

View File

@ -54,6 +54,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,liburi-encode-perl
,libwww-perl
,libdata-uuid-perl
,libnumber-bytes-human
,mysql-client | virtual-mysql-client
,perl-modules
,php5-mysql, php5-gd, php5-apcu, php-apc

View File

@ -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

View File

@ -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*
---------------------

View File

@ -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

View File

@ -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
@ -61,7 +61,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdigest-sha-perl
,libsys-cpu-perl, libsys-cpuload-perl, libsys-meminfo-perl
,libdata-uuid-perl
,mysql-client | virtual-mysql-client
,libnumber-bytes-human-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 +71,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

View File

@ -66,9 +66,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

View File

@ -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

View File

@ -1,33 +1,55 @@
#! /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/* /usr/share/zoneminder/www/cache
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
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
@ -37,25 +59,19 @@ if [ "$1" = "configure" ]; then
else
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
else
echo 'NOTE: mysql 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

View File

@ -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

View File

@ -71,22 +71,22 @@ 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)
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 900MB of memory.
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 +128,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>`__

View File

@ -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

View File

@ -11,3 +11,4 @@ Contents:
debian
redhat
multiserver
dedicateddrive

View File

@ -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.

View File

@ -0,0 +1,6 @@
check process zmdc.pl with pidfile /run/zm/zm.pid
if failed unixsocket /run/zm/zmdc2.sock then restart
group zm
start program = "/bin/systemctl start zoneminder"
stop program = "/bin/systemctl stop zoneminder"
#if 4 restarts within 20 cycles then timeout

View File

@ -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.

View File

@ -47,7 +47,7 @@ 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} );
}
@ -83,7 +83,7 @@ sub open {
sub close {
my $self = shift;
Fatal( "No close method defined for protocol ".$self->{name} );
Error( "No close method defined for protocol ".$self->{name} );
}
sub loadMonitor {

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,195 @@
# ==========================================================================
#
# 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 close
{
my $self = shift;
$self->{state} = 'closed';
}
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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -54,28 +54,6 @@ my $ChannelID = 1; # Usually...
my $DefaultFocusSpeed = 50; # Should be between 1 and 100
my $DefaultIrisSpeed = 50; # Should be between 1 and 100
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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,603 @@
# ==========================================================================
#
# 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 close
{
my $self = shift;
$self->{state} = 'closed';
}
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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -35,6 +35,7 @@ require Date::Manip;
require File::Find;
require File::Path;
require File::Copy;
require Number::Bytes::Human;
#our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object);
@ -300,8 +301,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 ) {
@ -351,9 +352,9 @@ sub delete {
$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 +376,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.');
}
# 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 +415,35 @@ 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*$/ );
Debug("Trying to delete from S3 on $aws_host / $aws_bucket ($$Storage{Url})");
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' ) {
@ -425,6 +457,9 @@ sub delete_files {
} # end sub delete_files
sub Storage {
if ( @_ > 1 ) {
$_[0]{Storage} = $_[1];
}
if ( ! $_[0]{Storage} ) {
$_[0]{Storage} = new ZoneMinder::Storage( $_[0]{StorageId} );
}
@ -470,7 +505,7 @@ sub DiskSpace {
$_[0]{DiskSpace} = $size;
Debug("DiskSpace for event $_[0]{Id} at $_[0]{Path} Updated to $size bytes");
} else {
Warning("DiskSpace: Event does not exist at $_[0]{Path}:" . $Event->to_string() );
Warning("DiskSpace: Event does not exist at $_[0]{Path}:" . $_[0]->to_string() );
}
} # end if ! defined DiskSpace
return $_[0]{DiskSpace};
@ -486,8 +521,7 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit();
return "Event has already been moved by someone else.";
}
my $OldStorage = $self->Storage();
my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
$$self{Storage} = $NewStorage;
@ -535,10 +569,15 @@ sub MoveTo {
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 = " . Number::Bytes::Human::format_bytes($size/$duration) . "/sec");
} # end foreach file.
if ( $error ) {

View File

@ -205,7 +205,7 @@ 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' ) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
@ -276,7 +276,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 '=[]' ) {

View File

@ -75,7 +75,7 @@ our %EXPORT_TAGS = (
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
our @EXPORT = qw();
@ -135,18 +135,19 @@ sub new {
$this->{initialised} = undef;
#$this->{id} = 'zmundef';
( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|;
$this->{idRoot} = $this->{id};
$this->{idArgs} = '';
$this->{level} = INFO;
# Detect if we are running in a terminal session, if so, default log level to INFO
$this->{hasTerm} = -t STDERR;
if ( $this->{hasTerm} ) {
$this->{termLevel} = INFO;
} else {
$this->{termLevel} = NOLOG;
}
if ( $this->{hasTerm} ) {
$this->{termLevel} = INFO;
} else {
$this->{termLevel} = NOLOG;
}
$this->{databaseLevel} = NOLOG;
$this->{fileLevel} = NOLOG;
$this->{syslogLevel} = NOLOG;
@ -156,7 +157,7 @@ if ( $this->{hasTerm} ) {
( $this->{fileName} = $0 ) =~ s|^.*/||;
$this->{logPath} = $Config{ZM_PATH_LOGS};
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.".log";
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
$this->{trace} = 0;
@ -175,9 +176,9 @@ sub BEGIN {
ZM_LOG_LEVEL_FILE => 0,
ZM_LOG_LEVEL_SYSLOG => 0,
ZM_LOG_DEBUG => 0,
ZM_LOG_DEBUG_TARGET => "",
ZM_LOG_DEBUG_TARGET => '',
ZM_LOG_DEBUG_LEVEL => 1,
ZM_LOG_DEBUG_FILE => ""
ZM_LOG_DEBUG_FILE => ''
);
while ( my ( $name, $value ) = each( %dbgConfig ) ) {
*{$name} = sub { $value };
@ -196,13 +197,13 @@ sub initialise( @ ) {
my $this = shift;
my %options = @_;
$this->{id} = $options{id} if ( defined($options{id}) );
$this->{id} = $options{id} if defined($options{id});
$this->{logPath} = $options{logPath} if ( defined($options{logPath}) );
$this->{logPath} = $options{logPath} if defined($options{logPath});
my $tempLogFile;
$tempLogFile = $this->{logPath}.'/'.$this->{id}.".log";
$tempLogFile = $options{logFile} if ( defined($options{logFile}) );
$tempLogFile = $this->{logPath}.'/'.$this->{id}.'.log';
$tempLogFile = $options{logFile} if defined($options{logFile});
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
$tempLogFile = $logFile;
}
@ -213,7 +214,7 @@ sub initialise( @ ) {
my $tempFileLevel = $this->{fileLevel};
my $tempSyslogLevel = $this->{syslogLevel};
$tempTermLevel = $options{termLevel} if ( defined($options{termLevel}) );
$tempTermLevel = $options{termLevel} if defined($options{termLevel});
if ( defined($options{databaseLevel}) ) {
$tempDatabaseLevel = $options{databaseLevel};
} else {
@ -230,16 +231,16 @@ sub initialise( @ ) {
$tempSyslogLevel = $Config{ZM_LOG_LEVEL_SYSLOG};
}
if ( defined($ENV{'LOG_PRINT'}) ) {
$tempTermLevel = $ENV{'LOG_PRINT'}? DEBUG : NOLOG;
if ( defined($ENV{LOG_PRINT}) ) {
$tempTermLevel = $ENV{LOG_PRINT}? DEBUG : NOLOG;
}
my $level;
$tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) );
$tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) );
$tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) );
$tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) );
$tempSyslogLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG')) );
$tempLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL'));
$tempTermLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM'));
$tempDatabaseLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE'));
$tempFileLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE'));
$tempSyslogLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG'));
if ( $Config{ZM_LOG_DEBUG} ) {
foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) {
@ -247,11 +248,11 @@ sub initialise( @ ) {
|| $target eq '_'.$this->{id}
|| $target eq $this->{idRoot}
|| $target eq '_'.$this->{idRoot}
|| $target eq ""
|| $target eq ''
) {
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
$tempLevel = $this->limit( $Config{ZM_LOG_DEBUG_LEVEL} );
if ( $Config{ZM_LOG_DEBUG_FILE} ne "" ) {
if ( $Config{ZM_LOG_DEBUG_FILE} ne '' ) {
$tempLogFile = $Config{ZM_LOG_DEBUG_FILE};
$tempFileLevel = $tempLevel;
}
@ -269,9 +270,9 @@ sub initialise( @ ) {
$this->level( $tempLevel );
$this->{trace} = $options{trace} if ( defined($options{trace}) );
$this->{trace} = $options{trace} if defined($options{trace});
$this->{autoFlush} = $ENV{'LOG_FLUSH'}?1:0 if ( defined($ENV{'LOG_FLUSH'}) );
$this->{autoFlush} = $ENV{LOG_FLUSH}?1:0 if defined($ENV{LOG_FLUSH});
$this->{initialised} = !undef;
@ -303,28 +304,28 @@ sub reinitialise {
# Bit of a nasty hack to reopen connections to log files and the DB
my $syslogLevel = $this->syslogLevel();
$this->syslogLevel( NOLOG );
$this->syslogLevel( $syslogLevel ) if ( $syslogLevel > NOLOG );
$this->syslogLevel($syslogLevel) if $syslogLevel > NOLOG;
my $logfileLevel = $this->fileLevel();
$this->fileLevel( NOLOG );
$this->fileLevel( $logfileLevel ) if ( $logfileLevel > NOLOG );
$this->fileLevel(NOLOG);
$this->fileLevel($logfileLevel) if $logfileLevel > NOLOG;
my $databaseLevel = $this->databaseLevel();
$this->databaseLevel( NOLOG );
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG );
$this->databaseLevel(NOLOG);
$this->databaseLevel($databaseLevel) if $databaseLevel > NOLOG;
my $screenLevel = $this->termLevel();
$this->termLevel( NOLOG );
$this->termLevel( $screenLevel ) if ( $screenLevel > NOLOG );
$this->termLevel(NOLOG);
$this->termLevel($screenLevel) if $screenLevel > NOLOG;
}
# Prevents undefined logging levels
sub limit {
my $this = shift;
my $level = shift;
return( DEBUG ) if ( $level > DEBUG );
return( NOLOG ) if ( $level < NOLOG );
return( $level );
return(DEBUG) if $level > DEBUG;
return(NOLOG) if $level < NOLOG;
return($level);
}
sub getTargettedEnv {
@ -332,32 +333,32 @@ sub getTargettedEnv {
my $name = shift;
my $envName = $name.'_'.$this->{id};
my $value;
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
if ( !defined($value) && $this->{id} ne $this->{idRoot} ) {
$value = $ENV{$envName} if defined($ENV{$envName});
if ( !defined($value) and ($this->{id} ne $this->{idRoot}) ) {
$envName = $name.'_'.$this->{idRoot};
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
$value = $ENV{$envName} if defined($ENV{$envName});
}
if ( !defined($value) ) {
$value = $ENV{$name} if ( defined($ENV{$name}) );
$value = $ENV{$name} if defined($ENV{$name});
}
if ( defined($value) ) {
( $value ) = $value =~ m/(.*)/;
}
return( $value );
return $value;
}
sub fetch {
if ( !$logger ) {
$logger = ZoneMinder::Logger->new();
$logger->initialise( 'syslogLevel'=>INFO, 'databaseLevel'=>INFO );
$logger->initialise( syslogLevel=>INFO, databaseLevel=>INFO );
}
return( $logger );
return $logger;
}
sub id {
my $this = shift;
my $id = shift;
if ( defined($id) && $this->{id} ne $id ) {
if ( defined($id) and ($this->{id} ne $id) ) {
# Remove whitespace
$id =~ s/\S//g;
# Replace non-alphanum with underscore
@ -371,37 +372,37 @@ sub id {
}
}
}
return( $this->{id} );
return $this->{id};
}
sub level {
my $this = shift;
my $level = shift;
if ( defined($level) ) {
$this->{level} = $this->limit( $level );
$this->{level} = $this->limit($level);
# effectiveLevel is the highest logging level used by any of the outputs.
$this->{effectiveLevel} = NOLOG;
$this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} );
$this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} );
$this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} );
$this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{effectiveLevel} );
$this->{effectiveLevel} = $this->{termLevel} if $this->{termLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{databaseLevel} if $this->{databaseLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{fileLevel} if $this->{fileLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{syslogLevel} if $this->{syslogLevel} > $this->{effectiveLevel};
# ICON: I am remarking this out because I don't see the point of having an effective level, if we are just going to set it to level.
#$this->{effectiveLevel} = $this->{level} if ( $this->{level} > $this->{effectiveLevel} );
}
return( $this->{level} );
return $this->{level};
}
sub debugOn {
my $this = shift;
return( $this->{effectiveLevel} >= DEBUG );
return $this->{effectiveLevel} >= DEBUG;
}
sub trace {
my $this = shift;
$this->{trace} = $_[0] if ( @_ );
return( $this->{trace} );
$this->{trace} = $_[0] if @_;
return $this->{trace};
}
sub termLevel {
@ -409,63 +410,62 @@ sub termLevel {
my $termLevel = shift;
if ( defined($termLevel) ) {
# What is the point of this next lint if we are just going to overwrite it with the next line? I propose we move it down one line or remove it altogether
$termLevel = NOLOG if ( !$this->{hasTerm} );
$termLevel = $this->limit( $termLevel );
$termLevel = NOLOG if !$this->{hasTerm};
$termLevel = $this->limit($termLevel);
if ( $this->{termLevel} != $termLevel ) {
$this->{termLevel} = $termLevel;
}
}
return( $this->{termLevel} );
return $this->{termLevel};
}
sub databaseLevel {
my $this = shift;
my $databaseLevel = shift;
if ( defined($databaseLevel) ) {
$databaseLevel = $this->limit( $databaseLevel );
$databaseLevel = $this->limit($databaseLevel);
if ( $this->{databaseLevel} != $databaseLevel ) {
if ( $databaseLevel > NOLOG && $this->{databaseLevel} <= NOLOG ) {
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;
$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}
$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}
$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 '"
Error( 'Unable to write log entries to DB, can\'t connect to database '
.$Config{ZM_DB_NAME}
."' on host '"
.' on host '
.$Config{ZM_DB_HOST}
."'"
);
} else {
$this->{dbh}->{AutoCommit} = 1;
Fatal( "Can't set AutoCommit on in database connection" )
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" )
Fatal('Can\'t set mysql_auto_reconnect on in database connection' )
unless( $this->{dbh}->{mysql_auto_reconnect} );
$this->{dbh}->trace( 0 );
}
@ -479,7 +479,7 @@ sub databaseLevel {
$this->{databaseLevel} = $databaseLevel;
}
}
return( $this->{databaseLevel} );
return $this->{databaseLevel};
}
sub fileLevel {
@ -487,13 +487,12 @@ sub fileLevel {
my $fileLevel = shift;
if ( defined($fileLevel) ) {
$fileLevel = $this->limit($fileLevel);
if ( $this->{fileLevel} != $fileLevel ) {
$this->closeFile() if ( $this->{fileLevel} > NOLOG );
$this->{fileLevel} = $fileLevel;
$this->openFile() if ( $this->{fileLevel} > NOLOG );
}
# The filename might have changed, so always close and re-open
$this->closeFile() if ( $this->{fileLevel} > NOLOG );
$this->{fileLevel} = $fileLevel;
$this->openFile() if ( $this->{fileLevel} > NOLOG );
}
return( $this->{fileLevel} );
return $this->{fileLevel};
}
sub syslogLevel {
@ -512,7 +511,7 @@ sub syslogLevel {
sub openSyslog {
my $this = shift;
openlog( $this->{id}, 'pid', "local1" );
openlog( $this->{id}, 'pid', 'local1' );
}
sub closeSyslog {
@ -532,27 +531,25 @@ sub logFile {
sub openFile {
my $this = shift;
if ( open( $LOGFILE, ">>", $this->{logFile} ) ) {
$LOGFILE->autoflush() if ( $this->{autoFlush} );
if ( open($LOGFILE, '>>', $this->{logFile}) ) {
$LOGFILE->autoflush() if $this->{autoFlush};
my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2];
my $webGid = (getgrnam( $Config{ZM_WEB_GROUP} ))[2];
my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2];
my $webGid = (getgrnam($Config{ZM_WEB_GROUP}))[2];
if ( $> == 0 ) {
chown( $webUid, $webGid, $this->{logFile} )
or Fatal( "Can't change permissions on log file '"
.$this->{logFile}."': $!"
)
or Fatal("Can't change permissions on log file $$this{logFile}: $!");
}
} else {
$this->fileLevel( NOLOG );
$this->termLevel( INFO );
Error( "Can't open log file '".$this->{logFile}."': $!" );
$this->fileLevel(NOLOG);
$this->termLevel(INFO);
Error("Can't open log file $$this{logFile}: $!");
}
}
sub closeFile {
my $this = shift;
close( $LOGFILE ) if ( fileno($LOGFILE) );
#my $this = shift;
close($LOGFILE) if fileno($LOGFILE);
}
sub logPrint {
@ -568,7 +565,7 @@ sub logPrint {
my ($seconds, $microseconds) = gettimeofday();
my $message = sprintf(
'%s.%06d %s[%d].%s [%s]'
, strftime( '%x %H:%M:%S' ,localtime( $seconds ) )
, strftime('%x %H:%M:%S', localtime($seconds))
, $microseconds
, $this->{id}
, $$
@ -576,42 +573,43 @@ sub logPrint {
, $string
);
if ( $this->{trace} ) {
$message = Carp::shortmess( $message );
$message = Carp::shortmess($message);
} else {
$message = $message."\n";
}
if ( $level <= $this->{syslogLevel} ) {
syslog( $priorities{$level}, $code." [%s]", $string );
syslog($priorities{$level}, $code.' [%s]', $string);
}
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} );
print($LOGFILE $message) if $level <= $this->{fileLevel};
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 );
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;
Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->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->{sth}->errstr());
}
}
my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0)
, $this->{id}
, $$
, $level
, $code
, $string
, $this->{fileName}
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;
Fatal( "Can't execute log entry '$sql': ".$this->{sth}->errstr() );
}
}
print( STDERR $message ) if ( $level <= $this->{termLevel} );
}
} # end if doing db logging
print(STDERR $message) if $level <= $this->{termLevel};
} # end if level < effectivelevel
}
sub logInit( ;@ ) {
my %options = @_ ? @_ : ();
$logger = ZoneMinder::Logger->new() if !$logger;
$logger->initialise( %options );
$logger->initialise(%options);
}
sub logReinit {
@ -619,14 +617,14 @@ sub logReinit {
}
sub logTerm {
return unless ( $logger );
return unless $logger;
$logger->terminate();
$logger = undef;
}
sub logHupHandler {
my $savedErrno = $!;
return unless( $logger );
return unless $logger;
fetch()->reinitialise();
logSetSignal();
$! = $savedErrno;
@ -641,85 +639,87 @@ sub logClearSignal {
}
sub logLevel {
return( fetch()->level( @_ ) );
return fetch()->level(@_);
}
sub logDebugging {
return( fetch()->debugOn() );
return fetch()->debugOn();
}
sub logTermLevel {
return( fetch()->termLevel( @_ ) );
return fetch()->termLevel(@_);
}
sub logDatabaseLevel {
return( fetch()->databaseLevel( @_ ) );
return fetch()->databaseLevel(@_);
}
sub logFileLevel {
return( fetch()->fileLevel( @_ ) );
return fetch()->fileLevel(@_);
}
sub logSyslogLevel {
return( fetch()->syslogLevel( @_ ) );
return fetch()->syslogLevel(@_);
}
sub Mark {
my $level = shift;
$level = DEBUG unless( defined($level) );
$level = DEBUG unless defined($level);
my $tag = 'Mark';
fetch()->logPrint( $level, $tag );
fetch()->logPrint($level, $tag);
}
sub Dump {
my $var = shift;
my $label = shift;
$label = 'VAR' unless( defined($label) );
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
$label = 'VAR' unless defined($label);
fetch()->logPrint(DEBUG, Data::Dumper->Dump([ $var ], [ $label ]));
}
sub debug {
my $log = shift;
$log->logPrint( DEBUG, @_ );
$log->logPrint(DEBUG, @_);
}
sub Debug( @ ) {
fetch()->logPrint( DEBUG, @_ );
fetch()->logPrint(DEBUG, @_);
}
sub Info( @ ) {
fetch()->logPrint( INFO, @_ );
fetch()->logPrint(INFO, @_);
}
sub info {
my $log = shift;
$log->logPrint( INFO, @_ );
$log->logPrint(INFO, @_);
}
sub Warning( @ ) {
fetch()->logPrint( WARNING, @_ );
fetch()->logPrint(WARNING, @_);
}
sub warn {
my $log = shift;
$log->logPrint( WARNING, @_ );
$log->logPrint(WARNING, @_);
}
sub Error( @ ) {
fetch()->logPrint( ERROR, @_ );
fetch()->logPrint(ERROR, @_);
}
sub error {
my $log = shift;
$log->logPrint( ERROR, @_ );
$log->logPrint(ERROR, @_);
}
sub Fatal( @ ) {
fetch()->logPrint( FATAL, @_ );
exit( -1 );
fetch()->logPrint(FATAL, @_);
if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) {
$SIG{TERM}();
}
exit(-1);
}
sub Panic( @ ) {
fetch()->logPrint( PANIC, @_ );
confess( $_[0] );
fetch()->logPrint(PANIC, @_);
confess($_[0]);
}
1;
@ -738,10 +738,10 @@ logInit( 'myproc', DEBUG );
Debug( 'This is what is happening' );
Info( 'Something interesting is happening' );
Warning( "Something might be going wrong." );
Error( "Something has gone wrong!!" );
Fatal( "Something has gone badly wrong, gotta stop!!" );
Panic( "Something fundamental has gone wrong, die with stack trace );
Warning( 'Something might be going wrong.' );
Error( 'Something has gone wrong!!' );
Fatal( 'Something has gone badly wrong, gotta stop!!' );
Panic( 'Something fundamental has gone wrong, die with stack trace' );
=head1 DESCRIPTION

View File

@ -166,6 +166,8 @@ 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 +317,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 +828,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

View File

@ -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 {
@ -156,7 +157,7 @@ sub lock_and_load {
if ( $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 ) {

View File

@ -159,14 +159,14 @@ MAIN: while( $loop ) {
while ( ! ( $dbh and $dbh->ping() ) ) {
$dbh = zmDbConnect();
if ( ! $dbh ) {
Error('Unable to connect to database');
if ( $continuous ) {
Error('Unable to connect to database');
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
Fatal('Unable to connect to database');
Term();
} # end if
} # end if
} # end while can't connect to the db
@ -175,13 +175,15 @@ MAIN: while( $loop ) {
if ( defined $storage_id ) {
@Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id );
if ( !@Storage_Areas ) {
Fatal("No Storage Area found with Id $storage_id");
Error("No Storage Area found with Id $storage_id");
Term();
}
Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}");
} elsif ( $Config{ZM_SERVER_ID} ) {
@Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} );
if ( ! @Storage_Areas ) {
Fatal("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID});
Error("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID});
Term();
}
Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name());
} else {
@ -465,10 +467,10 @@ MAIN: while( $loop ) {
} # end if exists in filesystem
} # end if ! in fs_events
} # foreach db_event
} else {
my $Monitor = new ZoneMinder::Monitor( $db_monitor );
my $Storage = $Monitor->Storage();
aud_print( "Database monitor '$db_monitor' does not exist in filesystem, should have been at ".$Storage->Path().'/'.$Monitor->Id()."\n" );
#} else {
#my $Monitor = new ZoneMinder::Monitor( $db_monitor );
#my $Storage = $Monitor->Storage();
#aud_print( "Database monitor '$db_monitor' does not exist in filesystem, should have been at ".$Storage->Path().'/'.$Monitor->Id()."\n" );
#if ( confirm() )
#{
# We don't actually do this in case it's new
@ -478,48 +480,62 @@ MAIN: while( $loop ) {
#}
}
} # end foreach db_monitor
redo MAIN if ( $cleaned );
if ( $cleaned ) {
Debug("Have done some cleaning, restarting.");
redo MAIN;
}
# Remove orphaned events (with no monitor)
$cleaned = 0;
Debug("Checking for Orphaned Events");
my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name
FROM Events LEFT JOIN Monitors ON (Events.MonitorId = Monitors.Id)
WHERE isnull(Monitors.Id)';
my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql )
or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
or Error( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
$res = $selectOrphanedEventsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
or Error( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() ) {
aud_print( "Found orphaned event with no monitor '$event->{Id}'" );
if ( confirm() ) {
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
if ( $res = $deleteEventSth->execute( $event->{Id} ) ) {
$cleaned = 1;
} else {
Error( "Can't execute: ".$deleteEventSth->errstr() );
}
}
}
redo MAIN if ( $cleaned );
redo MAIN if $cleaned;
# Remove empty events (with no frames)
$cleaned = 0;
Debug("Checking for Events with no Frames");
my $selectEmptyEventsSql = 'SELECT E.Id AS Id, E.StartTime, F.EventId FROM Events as E LEFT JOIN Frames as F ON (E.Id = F.EventId)
WHERE isnull(F.EventId) AND now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second > E.StartTime';
my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql )
or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
$res = $selectEmptyEventsSth->execute()
or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() );
while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) {
aud_print( "Found empty event with no frame records '$event->{Id}' at $$event{StartTime}" );
if ( confirm() ) {
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
if ( my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) ) {
if ( $res = $selectEmptyEventsSth->execute() ) {
while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) {
aud_print( "Found empty event with no frame records '$event->{Id}' at $$event{StartTime}" );
if ( confirm() ) {
if ( $res = $deleteEventSth->execute( $event->{Id} ) ) {
$cleaned = 1;
} else {
Error( "Can't execute: ".$deleteEventSth->errstr() );
}
}
} # end foreach row
} else {
Error( "Can't execute: ".$selectEmptyEventsSth->errstr() );
}
} else {
Error( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
}
redo MAIN if ( $cleaned );
redo MAIN if $cleaned;
# Remove orphaned frame records
$cleaned = 0;
Debug("Checking for Orphaned Frames");
my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)';
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
@ -534,10 +550,11 @@ MAIN: while( $loop ) {
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
redo MAIN if $cleaned;
# Remove orphaned stats records
$cleaned = 0;
Debug("Checking for Orphaned Stats");
my $selectOrphanedStatsSql = 'SELECT DISTINCT EventId FROM Stats
WHERE EventId NOT IN (SELECT Id FROM Events)';
my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql )
@ -571,11 +588,15 @@ MAIN: while( $loop ) {
#;
'SELECT *, unix_timestamp(StartTime) AS TimeStamp FROM Events WHERE EndTime IS NULL AND StartTime < (now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second)';
my $selectFrameDataSql = 'SELECT max(TimeStamp) as EndTime, unix_timestamp(max(TimeStamp)) AS EndTimeStamp, max(FrameId) as Frames,
count(if(Score>0,1,NULL)) as AlarmFrames,
sum(Score) as TotScore,
max(Score) as MaxScore
FROM Frames WHERE EventId=?';
my $selectFrameDataSql = '
SELECT
max(TimeStamp) as EndTime,
unix_timestamp(max(TimeStamp)) AS EndTimeStamp,
max(FrameId) as Frames,
count(if(Score>0,1,NULL)) as AlarmFrames,
sum(Score) as TotScore,
max(Score) as MaxScore
FROM Frames WHERE EventId=?';
my $selectFrameDataSth = $dbh->prepare_cached($selectFrameDataSql)
or Fatal( "Can't prepare '$selectFrameDataSql': ".$dbh->errstr() );
@ -599,9 +620,12 @@ MAIN: while( $loop ) {
$res = $selectUnclosedEventsSth->execute()
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) {
aud_print( "Found open event '$event->{Id}'" );
aud_print( "Found open event '$event->{Id}' at $$event{StartTime}" );
if ( confirm( 'close', 'closing' ) ) {
$res = $selectFrameDataSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$selectFrameDataSth->errstr() );
if ( ! ( $res = $selectFrameDataSth->execute($event->{Id}) ) ) {
Error( "Can't execute: $selectFrameDataSql:".$selectFrameDataSth->errstr() );
next;
}
my $frame = $selectFrameDataSth->fetchrow_hashref();
if ( $frame ) {
$res = $updateUnclosedEventsSth->execute(
@ -622,12 +646,13 @@ MAIN: while( $loop ) {
$frame->{MaxScore},
RECOVER_TEXT,
$event->{Id}
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
) or Error( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
} else {
Error('SHOULD DELETE');
} # end if has frame data
}
} # end while unclosed event
Debug("Done closing open events.");
# Now delete any old image files
if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) {
@ -664,6 +689,11 @@ MAIN: while( $loop ) {
}
} else {
# Time of record
# 7 days is invalid. We need to remove the s
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^(.*)s$/ ) {
$Config{ZM_LOG_DATABASE_LIMIT} = $1;
}
my $deleteLogByTimeSql =
'DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.')';
@ -683,14 +713,6 @@ MAIN: while( $loop ) {
UPDATE Monitors SET
TotalEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id),
TotalEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
HourEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB( NOW(), INTERVAL 1 hour) ),
HourEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 hour) AND DiskSpace IS NOT NULL),
DayEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 day)),
DayEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 day) AND DiskSpace IS NOT NULL),
WeekEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 week)),
WeekEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 week) AND DiskSpace IS NOT NULL),
MonthEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB( NOW(), INTERVAL 1 month)),
MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 month) AND DiskSpace IS NOT NULL),
ArchivedEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1),
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1 AND DiskSpace IS NOT NULL)
`;

View File

@ -104,6 +104,10 @@ 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" );
@ -236,8 +240,8 @@ use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad;
#use Data::Dumper;
# We count 100 of these, so total timeout is this value *100.
use constant KILL_DELAY => 100*1000; # 1/10th of a second
# We count 10 of these, so total timeout is this value *10.
use constant KILL_DELAY => 1; # seconds
our %cmd_hash;
our %pid_hash;
@ -289,9 +293,8 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) {
require ZoneMinder::Server;
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record' );
$Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} );
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name} );
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name} );
}
while( 1 ) {
@ -300,7 +303,7 @@ sub run {
if ( ! ( $secs_count % 60 ) ) {
$dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load();
dPrint( ZoneMinder::Logger::INFO, 'Updating Server record' );
dPrint( ZoneMinder::Logger::DEBUG, 'Updating Server record' );
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());
@ -385,6 +388,7 @@ sub cPrint {
}
}
# I think the purpose of this is to echo the logs to the client process so it can then display them.
sub dPrint {
my $logLevel = shift;
if ( fileno(CLIENT) ) {
@ -523,7 +527,7 @@ sub kill_until_dead {
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++ > 100 ) {
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"
@ -532,8 +536,9 @@ sub kill_until_dead {
last;
}
# THe purpose of the signal blocking is to simplify the concurrency
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
usleep( KILL_DELAY );
sleep( KILL_DELAY );
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n";
}
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";

View File

@ -232,6 +232,7 @@ if ( $command =~ /^(?:start|restart)$/ ) {
# This is now started unconditionally
runCommand('zmdc.pl start zmfilter.pl');
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.");
@ -256,6 +257,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 {

View File

@ -83,10 +83,10 @@ while( 1 ) {
$dbh = zmDbConnect();
}
$dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)');
$dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)');
$dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)');
$dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)');
$dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr());
sleep( $Config{ZM_STATS_UPDATE_INTERVAL} );
} # end while (1)

View File

@ -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' )) {

View File

@ -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

View File

@ -23,113 +23,127 @@
#include "zm.h"
#include "zm_db.h"
// From what I read, we need one of these per thread
MYSQL dbconn;
Mutex db_mutex;
int zmDbConnected = false;
bool zmDbConnected = false;
void zmDbConnect() {
bool zmDbConnect() {
// For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu
//if ( zmDbConnected )
//return;
if ( !mysql_init( &dbconn ) ) {
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
if ( !mysql_init(&dbconn) ) {
Error("Can't initialise database connection: %s", mysql_error(&dbconn));
return false;
}
my_bool reconnect = 1;
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) )
Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn));
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL );
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
mysql_ssl_set(&dbconn,
staticConfig.DB_SSL_CLIENT_KEY.c_str(),
staticConfig.DB_SSL_CLIENT_CERT.c_str(),
staticConfig.DB_SSL_CA_CERT.c_str(),
NULL, NULL);
std::string::size_type colonIndex = staticConfig.DB_HOST.find(":");
if ( colonIndex == std::string::npos ) {
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
if ( !mysql_real_connect(&dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0) ) {
Error( "Can't connect to server: %s", mysql_error(&dbconn));
return false;
}
} else {
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 );
if ( dbPortOrSocket[0] == '/' ) {
if ( !mysql_real_connect( &dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
if ( !mysql_real_connect(&dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0) ) {
Error("Can't connect to server: %s", mysql_error(&dbconn));
return false;
}
} else {
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
return false;
}
}
}
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) {
Error( "Can't select database: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
return false;
}
zmDbConnected = true;
return zmDbConnected;
}
void zmDbClose() {
if ( zmDbConnected ) {
db_mutex.lock();
mysql_close( &dbconn );
// mysql_init() call implicitly mysql_library_init() but
// mysql_close() does not call mysql_library_end()
mysql_library_end();
zmDbConnected = false;
db_mutex.unlock();
}
}
MYSQL_RES * zmDbFetch( const char * query ) {
MYSQL_RES * zmDbFetch(const char * query) {
if ( ! zmDbConnected ) {
Error( "Not connected." );
Error("Not connected.");
return NULL;
}
db_mutex.lock();
if ( mysql_query( &dbconn, query ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
if ( mysql_query(&dbconn, query) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock();
return NULL;
}
Debug( 4, "Success running query: %s", query );
MYSQL_RES *result = mysql_store_result( &dbconn );
Debug(4, "Success running query: %s", query);
MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) {
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
return NULL;
Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query);
}
db_mutex.unlock();
return result;
} // end MYSQL_RES * zmDbFetch( const char * query );
} // end MYSQL_RES * zmDbFetch(const char * query);
zmDbRow *zmDbFetchOne( const char *query ) {
zmDbRow *zmDbFetchOne(const char *query) {
zmDbRow *row = new zmDbRow();
if ( row->fetch( query ) ) {
if ( row->fetch(query) ) {
return row;
}
delete row;
return NULL;
}
MYSQL_RES *zmDbRow::fetch( const char *query ) {
result_set = zmDbFetch( query );
MYSQL_RES *zmDbRow::fetch(const char *query) {
result_set = zmDbFetch(query);
if ( ! result_set ) return result_set;
int n_rows = mysql_num_rows( result_set );
int n_rows = mysql_num_rows(result_set);
if ( n_rows != 1 ) {
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
mysql_free_result( result_set );
Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query);
mysql_free_result(result_set);
result_set = NULL;
return result_set;
}
row = mysql_fetch_row( result_set );
row = mysql_fetch_row(result_set);
if ( ! row ) {
mysql_free_result( result_set );
mysql_free_result(result_set);
result_set = NULL;
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn));
} else {
Debug(5, "Success");
}
return result_set;
}
zmDbRow::~zmDbRow() {
if ( result_set )
mysql_free_result( result_set );
if ( result_set ) {
mysql_free_result(result_set);
result_set = NULL;
}
}

View File

@ -21,37 +21,32 @@
#define ZM_DB_H
#include <mysql/mysql.h>
#include "zm_thread.h"
class zmDbRow {
private:
MYSQL_RES *result_set;
MYSQL_ROW row;
public:
zmDbRow() { result_set = NULL; row = NULL; };
MYSQL_RES *fetch( const char *query );
zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
~zmDbRow();
private:
MYSQL_RES *result_set;
MYSQL_ROW row;
public:
zmDbRow() { result_set = NULL; row = NULL; };
MYSQL_RES *fetch( const char *query );
zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
~zmDbRow();
char *operator[](unsigned int index) const {
return row[index];
}
MYSQL_ROW mysql_row() const { return row; };
char *operator[](unsigned int index) const {
return row[index];
}
};
#ifdef __cplusplus
extern "C" {
#endif
extern MYSQL dbconn;
extern Mutex db_mutex;
extern int zmDbConnected;
void zmDbConnect();
bool zmDbConnect();
void zmDbClose();
MYSQL_RES * zmDbFetch( const char *query );
zmDbRow *zmDbFetchOne( const char *query );
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // ZM_DB_H

View File

@ -96,11 +96,14 @@ Event::Event(
monitor->GetOptSaveJPEGs(),
storage->SchemeString().c_str()
);
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql );
exit( mysql_errno( &dbconn ) );
db_mutex.unlock();
return;
}
id = mysql_insert_id( &dbconn );
db_mutex.unlock();
if ( untimedEvent ) {
Warning( "Event %d has zero time, setting to current", id );
}
@ -110,7 +113,6 @@ Event::Event(
tot_score = 0;
max_score = 0;
struct stat statbuf;
char id_file[PATH_MAX];
if ( storage->Scheme() == Storage::DEEP ) {
@ -225,22 +227,8 @@ Event::Event(
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
Event::~Event() {
static char sql[ZM_SQL_MED_BUFSIZ];
struct DeltaTimeval delta_time;
DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec );
if ( frames > last_db_frame ) {
Debug( 1, "Adding closing frame %d to DB", frames );
snprintf( sql, sizeof(sql),
"insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )",
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
// We cose the videowriter first, because it might take some time, and we don't want to lock the db if we can avoid it
/* Close the video file */
if ( videowriter != NULL ) {
@ -258,11 +246,34 @@ Event::~Event() {
}
}
snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
static char sql[ZM_SQL_MED_BUFSIZ];
struct DeltaTimeval delta_time;
DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec );
if ( frames > last_db_frame ) {
Debug( 1, "Adding closing frame %d to DB", frames );
snprintf( sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )",
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
} else {
Debug(1,"Success writing last frame");
}
db_mutex.unlock();
}
snprintf( sql, sizeof(sql), "UPDATE Events SET Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't update event: %s", mysql_error(&dbconn));
} else {
Debug(1,"Success updating event");
}
db_mutex.unlock();
}
void Event::createNotes( std::string &notes ) {
@ -427,9 +438,11 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() );
snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s", mysql_error( &dbconn ) );
}
db_mutex.unlock();
#endif
}
}
@ -490,9 +503,11 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
if ( frameCount ) {
Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames );
*(sql+strlen(sql)-2) = '\0';
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frames: %s, sql was (%s)", mysql_error( &dbconn ), sql );
}
db_mutex.unlock();
last_db_frame = frames;
} else {
Debug( 1, "No valid pre-capture frames to add" );
@ -542,10 +557,13 @@ Debug(3, "Writing video");
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
db_mutex.unlock();
return;
}
db_mutex.unlock();
last_db_frame = frames;
// We are writing a Bulk frame
@ -560,10 +578,11 @@ Debug(3, "Writing video");
max_score,
id
);
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
db_mutex.unlock();
}
} // end if db_frame

View File

@ -88,43 +88,48 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
}
bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) {
loadEventData( init_event_id );
loadEventData(init_event_id);
if ( init_frame_id ) {
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
curr_frame_id = init_frame_id;
if ( init_frame_id >= event_data->frame_count ) {
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count );
curr_stream_time = event_data->start_time;
} else {
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
curr_frame_id = init_frame_id;
}
} else {
curr_stream_time = event_data->start_time;
}
return( true );
return true;
}
bool EventStream::loadEventData( int event_id ) {
bool EventStream::loadEventData(int event_id) {
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id );
snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id);
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
MYSQL_RES *result = mysql_store_result( &dbconn );
MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
Error("Can't use query result: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
if ( !mysql_num_rows( result ) ) {
Fatal( "Unable to load event %d, not found in DB", event_id );
if ( !mysql_num_rows(result) ) {
Fatal("Unable to load event %d, not found in DB", event_id);
}
MYSQL_ROW dbrow = mysql_fetch_row( result );
MYSQL_ROW dbrow = mysql_fetch_row(result);
if ( mysql_errno( &dbconn ) ) {
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
if ( mysql_errno(&dbconn) ) {
Error("Can't fetch row: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
delete event_data;
@ -886,3 +891,17 @@ void EventStream::runStream() {
closeComms();
}
void EventStream::setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
loadInitialEventData( init_event_id, init_frame_id );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
return;
}
}
void EventStream::setStreamStart( int monitor_id, time_t event_time ) {
loadInitialEventData( monitor_id, event_time );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
return;
}
}

View File

@ -110,20 +110,8 @@ class EventStream : public StreamBase {
ffmpeg_input = NULL;
}
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
loadInitialEventData( init_event_id, init_frame_id );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
return;
}
}
void setStreamStart( int monitor_id, time_t event_time ) {
loadInitialEventData( monitor_id, event_time );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
return;
}
}
void setStreamStart( int init_event_id, unsigned int init_frame_id );
void setStreamStart( int monitor_id, time_t event_time );
void setStreamMode( StreamMode p_mode ) {
mode = p_mode;
}

View File

@ -109,7 +109,6 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
frameCount = 0;
startTime = 0;
mCanCapture = false;
mOpenStart = 0;
videoStore = NULL;
video_last_pts = 0;
have_video_keyframe = false;
@ -161,7 +160,7 @@ void FfmpegCamera::Terminate() {
int FfmpegCamera::PrimeCapture() {
if ( mCanCapture ) {
Info( "Priming capture from %s", mPath.c_str() );
Info( "Priming capture from %s, CLosing", mPath.c_str() );
CloseFfmpeg();
}
mVideoStreamId = -1;
@ -172,7 +171,6 @@ int FfmpegCamera::PrimeCapture() {
}
int FfmpegCamera::PreCapture() {
Debug(1, "PreCapture");
// If Reopen was called, then ffmpeg is closed and we need to reopen it.
if ( ! mCanCapture )
return OpenFfmpeg();
@ -189,7 +187,7 @@ int FfmpegCamera::Capture( Image &image ) {
int frameComplete = false;
while ( !frameComplete ) {
int avResult = av_read_frame( mFormatContext, &packet );
int avResult = av_read_frame(mFormatContext, &packet);
char errbuf[AV_ERROR_MAX_STRING_SIZE];
if ( avResult < 0 ) {
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
@ -199,13 +197,11 @@ int FfmpegCamera::Capture( Image &image ) {
// Check for Connection failure.
(avResult == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf );
ReopenFfmpeg();
continue;
Info("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf);
} else {
Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf);
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
return( -1 );
return -1;
}
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
@ -321,7 +317,6 @@ int FfmpegCamera::OpenFfmpeg() {
int ret;
mOpenStart = time(NULL);
have_video_keyframe = false;
// Open the input, not necessarily a file
@ -364,12 +359,21 @@ int FfmpegCamera::OpenFfmpeg() {
#endif
{
Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(errno));
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( mFormatContext );
#else
avformat_close_input( &mFormatContext );
#endif
mFormatContext = NULL;
av_dict_free(&opts);
return -1;
}
AVDictionaryEntry *e=NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key);
}
av_dict_free(&opts);
Debug(1, "Opened input");
@ -496,13 +500,20 @@ int FfmpegCamera::OpenFfmpeg() {
#endif
} // end if h264
#endif
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) {
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) {
Debug(1, "Failed to find decoder (h264_mmal)" );
} else {
Debug(1, "Success finding decoder (h264_mmal)" );
}
}
if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) {
// Try and get the codec from the codec context
Error("Can't find codec for video stream from %s", mPath.c_str());
return -1;
} else {
Debug(1, "Video Found decoder");
Debug(1, "Video Found decoder %s", mVideoCodec->name);
zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0);
// Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
@ -517,6 +528,7 @@ int FfmpegCamera::OpenFfmpeg() {
Warning( "Option %s not recognized by ffmpeg", e->key);
}
Error( "Unable to open codec for video stream from %s", mPath.c_str() );
av_dict_free(&opts);
return -1;
} else {
@ -524,6 +536,7 @@ int FfmpegCamera::OpenFfmpeg() {
if ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key);
}
av_dict_free(&opts);
}
}
@ -689,21 +702,20 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
while ( ! frameComplete ) {
av_init_packet( &packet );
ret = av_read_frame( mFormatContext, &packet );
ret = av_read_frame(mFormatContext, &packet);
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
if (
// Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(ret == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
ReopenFfmpeg();
Info("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf);
} else {
Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf);
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
return( -1 );
return -1;
}
int keyframe = packet.flags & AV_PKT_FLAG_KEY;

View File

@ -121,7 +121,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
}
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
Debug(1,"Packet is for our stream (%d)", packet.stream_index );
Debug(3,"Packet is for our stream (%d)", packet.stream_index );
AVCodecContext *context = streams[packet.stream_index].context;
@ -169,9 +169,9 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
frameComplete = 1;
# else
ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet );
ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet);
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
zm_av_packet_unref( &packet );
continue;
@ -179,7 +179,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
#endif
} // end if it's the right stream
zm_av_packet_unref( &packet );
zm_av_packet_unref( &packet );
} // end while ! frameComplete
return frame;

View File

@ -760,7 +760,7 @@ bool Image::ReadRaw( const char *filename ) {
return false;
}
if ( statbuf.st_size != size ) {
if ( (unsigned int)statbuf.st_size != size ) {
fclose(infile);
Error("Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size);
return false;

View File

@ -61,22 +61,22 @@ void Logger::usrHandler( int sig ) {
logger->level( logger->level()+1 );
else if ( sig == SIGUSR2 )
logger->level( logger->level()-1 );
Info( "Logger - Level changed to %d", logger->level() );
Info("Logger - Level changed to %d", logger->level());
}
Logger::Logger() :
mLevel( INFO ),
mTermLevel( NOLOG ),
mDatabaseLevel( NOLOG ),
mFileLevel( NOLOG ),
mSyslogLevel( NOLOG ),
mEffectiveLevel( NOLOG ),
mLevel(INFO),
mTerminalLevel(NOLOG),
mDatabaseLevel(NOLOG),
mFileLevel(NOLOG),
mSyslogLevel(NOLOG),
mEffectiveLevel(NOLOG),
//mLogPath( staticConfig.PATH_LOGS.c_str() ),
//mLogFile( mLogPath+"/"+mId+".log" ),
mDbConnected( false ),
mLogFileFP( NULL ),
mHasTerm( false ),
mFlush( false ) {
mDbConnected(false),
mLogFileFP(NULL),
mHasTerminal(false),
mFlush(false) {
if ( smInstance ) {
Panic( "Attempt to create second instance of Logger class" );
@ -98,7 +98,7 @@ Logger::Logger() :
char code[4] = "";
for ( int i = DEBUG1; i <= DEBUG9; i++ ) {
snprintf( code, sizeof(code), "DB%d", i );
snprintf(code, sizeof(code), "DB%d", i);
smCodes[i] = code;
smSyslogPriorities[i] = LOG_DEBUG;
}
@ -107,13 +107,14 @@ Logger::Logger() :
}
if ( fileno(stderr) && isatty(fileno(stderr)) )
mHasTerm = true;
mHasTerminal = true;
}
Logger::~Logger() {
terminate();
smCodes.clear();
smSyslogPriorities.clear();
smInitialised = false;
#if 0
for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) {
smCodes.erase( itr );
@ -124,15 +125,15 @@ Logger::~Logger() {
#endif
}
void Logger::initialise( const std::string &id, const Options &options ) {
void Logger::initialise(const std::string &id, const Options &options) {
char *envPtr;
if ( !id.empty() )
this->id( id );
this->id(id);
std::string tempLogFile;
if ( (envPtr = getTargettedEnv( "LOG_FILE" )) )
if ( (envPtr = getTargettedEnv("LOG_FILE")) )
tempLogFile = envPtr;
else if ( options.mLogFile.size() )
tempLogFile = options.mLogFile;
@ -144,21 +145,24 @@ void Logger::initialise( const std::string &id, const Options &options ) {
}
Level tempLevel = INFO;
Level tempTermLevel = mTermLevel;
Level tempTerminalLevel = mTerminalLevel;
Level tempDatabaseLevel = mDatabaseLevel;
Level tempFileLevel = mFileLevel;
Level tempSyslogLevel = mSyslogLevel;
if ( options.mTermLevel != NOOPT )
tempTermLevel = options.mTermLevel;
if ( options.mTerminalLevel != NOOPT )
tempTerminalLevel = options.mTerminalLevel;
if ( options.mDatabaseLevel != NOOPT )
tempDatabaseLevel = options.mDatabaseLevel;
else
tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database;
if ( options.mFileLevel != NOOPT )
tempFileLevel = options.mFileLevel;
else
tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file;
if ( options.mSyslogLevel != NOOPT )
tempSyslogLevel = options.mSyslogLevel;
else
@ -166,22 +170,22 @@ void Logger::initialise( const std::string &id, const Options &options ) {
// Legacy
if ( (envPtr = getenv( "LOG_PRINT" )) )
tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG;
tempTerminalLevel = atoi(envPtr) ? DEBUG9 : NOLOG;
if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) )
if ( (envPtr = getTargettedEnv("LOG_LEVEL")) )
tempLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) )
tempTermLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) )
if ( (envPtr = getTargettedEnv("LOG_LEVEL_TERM")) )
tempTerminalLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv("LOG_LEVEL_DATABASE")) )
tempDatabaseLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) )
if ( (envPtr = getTargettedEnv("LOG_LEVEL_FILE")) )
tempFileLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) )
if ( (envPtr = getTargettedEnv("LOG_LEVEL_SYSLOG")) )
tempSyslogLevel = atoi(envPtr);
if ( config.log_debug ) {
StringVector targets = split( config.log_debug_target, "|" );
StringVector targets = split(config.log_debug_target, "|");
for ( unsigned int i = 0; i < targets.size(); i++ ) {
const std::string &target = targets[i];
if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "" ) {
@ -198,20 +202,19 @@ void Logger::initialise( const std::string &id, const Options &options ) {
// if we don't have debug turned on, then the max effective log level is INFO
if ( tempSyslogLevel > INFO ) tempSyslogLevel = INFO;
if ( tempFileLevel > INFO ) tempFileLevel = INFO;
if ( tempTermLevel > INFO ) tempTermLevel = INFO;
if ( tempTerminalLevel > INFO ) tempTerminalLevel = INFO;
if ( tempDatabaseLevel > INFO ) tempDatabaseLevel = INFO;
if ( tempLevel > INFO ) tempLevel = INFO;
} // end if config.log_debug
logFile(tempLogFile);
logFile( tempLogFile );
terminalLevel(tempTerminalLevel);
databaseLevel(tempDatabaseLevel);
fileLevel(tempFileLevel);
syslogLevel(tempSyslogLevel);
termLevel( tempTermLevel );
databaseLevel( tempDatabaseLevel );
fileLevel( tempFileLevel );
syslogLevel( tempSyslogLevel );
level( tempLevel );
level(tempLevel);
mFlush = false;
if ( (envPtr = getenv("LOG_FLUSH")) ) {
@ -220,27 +223,27 @@ void Logger::initialise( const std::string &id, const Options &options ) {
mFlush = true;
}
//mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false;
{
struct sigaction action;
memset( &action, 0, sizeof(action) );
memset(&action, 0, sizeof(action));
action.sa_handler = usrHandler;
action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) {
Fatal( "sigaction(), error = %s", strerror(errno) );
// Does this REALLY need to be fatal?
if ( sigaction(SIGUSR1, &action, 0) < 0 ) {
Fatal("sigaction(), error = %s", strerror(errno));
}
if ( sigaction( SIGUSR2, &action, 0 ) < 0) {
Fatal( "sigaction(), error = %s", strerror(errno) );
if ( sigaction(SIGUSR2, &action, 0) < 0) {
Fatal("sigaction(), error = %s", strerror(errno));
}
}
mInitialised = true;
Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s",
Debug(1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s",
smCodes[mLevel].c_str(),
smCodes[mEffectiveLevel].c_str(),
smCodes[mTermLevel].c_str(),
smCodes[mTerminalLevel].c_str(),
smCodes[mDatabaseLevel].c_str(),
smCodes[mFileLevel].c_str(),
mLogFile.c_str(),
@ -249,7 +252,7 @@ void Logger::initialise( const std::string &id, const Options &options ) {
}
void Logger::terminate() {
Debug(1, "Terminating Logger" );
Debug(1, "Terminating Logger");
if ( mFileLevel > NOLOG )
closeFile();
@ -261,68 +264,69 @@ void Logger::terminate() {
closeDatabase();
}
bool Logger::boolEnv( const std::string &name, bool defaultValue ) {
const char *envPtr = getenv( name.c_str() );
return( envPtr ? atoi( envPtr ) : defaultValue );
// These don't belong here, they have nothing to do with logging
bool Logger::boolEnv(const std::string &name, bool defaultValue) {
const char *envPtr = getenv(name.c_str());
return envPtr ? atoi(envPtr) : defaultValue;
}
int Logger::intEnv( const std::string &name, bool defaultValue ) {
const char *envPtr = getenv( name.c_str() );
return( envPtr ? atoi( envPtr ) : defaultValue );
int Logger::intEnv(const std::string &name, bool defaultValue) {
const char *envPtr = getenv(name.c_str());
return envPtr ? atoi(envPtr) : defaultValue;
}
std::string Logger::strEnv( const std::string &name, const std::string &defaultValue ) {
const char *envPtr = getenv( name.c_str() );
return( envPtr ? envPtr : defaultValue );
std::string Logger::strEnv(const std::string &name, const std::string &defaultValue) {
const char *envPtr = getenv(name.c_str());
return envPtr ? envPtr : defaultValue;
}
char *Logger::getTargettedEnv( const std::string &name ) {
char *Logger::getTargettedEnv(const std::string &name) {
std::string envName;
envName = name+"_"+mId;
char *envPtr = getenv( envName.c_str() );
char *envPtr = getenv(envName.c_str());
if ( !envPtr && mId != mIdRoot ) {
envName = name+"_"+mIdRoot;
envPtr = getenv( envName.c_str() );
envPtr = getenv(envName.c_str());
}
if ( !envPtr )
envPtr = getenv( name.c_str() );
return( envPtr );
envPtr = getenv(name.c_str());
return envPtr;
}
const std::string &Logger::id( const std::string &id ) {
const std::string &Logger::id(const std::string &id) {
std::string tempId = id;
size_t pos;
// Remove whitespace
while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) {
tempId.replace( pos, 1, "" );
tempId.replace(pos, 1, "");
}
// Replace non-alphanum with underscore
while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) {
tempId.replace( pos, 1, "_" );
while ( (pos = tempId.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")) != std::string::npos ) {
tempId.replace(pos, 1, "_");
}
if ( mId != tempId ) {
mId = tempId;
pos = mId.find( '_' );
pos = mId.find('_');
if ( pos != std::string::npos ) {
mIdRoot = mId.substr( 0, pos );
mIdRoot = mId.substr(0, pos);
if ( ++pos < mId.size() )
mIdArgs = mId.substr( pos );
mIdArgs = mId.substr(pos);
}
}
return( mId );
return mId;
}
Logger::Level Logger::level( Logger::Level level ) {
Logger::Level Logger::level(Logger::Level level) {
if ( level > NOOPT ) {
level = limit(level);
if ( mLevel != level )
mLevel = level;
mEffectiveLevel = NOLOG;
if ( mTermLevel > mEffectiveLevel )
mEffectiveLevel = mTermLevel;
if ( mTerminalLevel > mEffectiveLevel )
mEffectiveLevel = mTerminalLevel;
if ( mDatabaseLevel > mEffectiveLevel )
mEffectiveLevel = mDatabaseLevel;
if ( mFileLevel > mEffectiveLevel )
@ -332,18 +336,18 @@ Logger::Level Logger::level( Logger::Level level ) {
if ( mEffectiveLevel > mLevel)
mEffectiveLevel = mLevel;
}
return( mLevel );
return mLevel;
}
Logger::Level Logger::termLevel( Logger::Level termLevel ) {
if ( termLevel > NOOPT ) {
if ( !mHasTerm )
termLevel = NOLOG;
termLevel = limit(termLevel);
if ( mTermLevel != termLevel )
mTermLevel = termLevel;
Logger::Level Logger::terminalLevel( Logger::Level terminalLevel ) {
if ( terminalLevel > NOOPT ) {
if ( !mHasTerminal )
terminalLevel = NOLOG;
terminalLevel = limit(terminalLevel);
if ( mTerminalLevel != terminalLevel )
mTerminalLevel = terminalLevel;
}
return( mTermLevel );
return mTerminalLevel;
}
Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
@ -357,21 +361,20 @@ Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
} // end if ( mDatabaseLevel != databaseLevel )
} // end if ( databaseLevel > NOOPT )
return( mDatabaseLevel );
return mDatabaseLevel;
}
Logger::Level Logger::fileLevel( Logger::Level fileLevel ) {
if ( fileLevel > NOOPT ) {
fileLevel = limit(fileLevel);
if ( mFileLevel != fileLevel ) {
if ( mFileLevel > NOLOG )
closeFile();
mFileLevel = fileLevel;
if ( mFileLevel > NOLOG )
openFile();
}
// Always close, because we may have changed file names
if ( mFileLevel > NOLOG )
closeFile();
mFileLevel = fileLevel;
if ( mFileLevel > NOLOG )
openFile();
}
return( mFileLevel );
return mFileLevel;
}
Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) {
@ -385,7 +388,7 @@ Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) {
openSyslog();
}
}
return( mSyslogLevel );
return mSyslogLevel;
}
void Logger::logFile( const std::string &logFile ) {
@ -402,9 +405,13 @@ void Logger::logFile( const std::string &logFile ) {
}
void Logger::openFile() {
if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"a" )) == (FILE *)NULL ) {
if ( mLogFile.size() ) {
if ( (mLogFileFP = fopen(mLogFile.c_str() ,"a")) == (FILE *)NULL ) {
mFileLevel = NOLOG;
Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) );
}
} else {
puts("Called Logger::openFile() without a filename");
}
}
@ -443,9 +450,9 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
const char *classString = smCodes[level].c_str();
if ( level < PANIC || level > DEBUG9 )
Panic( "Invalid logger level %d", level );
Panic("Invalid logger level %d", level);
gettimeofday( &timeVal, NULL );
gettimeofday(&timeVal, NULL);
#if 0
if ( logRuntime ) {
@ -457,8 +464,8 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
} else {
#endif
char *timePtr = timeString;
timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) );
snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec );
timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec));
snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec);
#if 0
}
#endif
@ -512,7 +519,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
char *syslogEnd = logPtr;
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
if ( level <= mTermLevel ) {
if ( level <= mTerminalLevel ) {
puts( logString );
fflush( stdout );
}
@ -524,12 +531,15 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
} else {
puts("Logging to file, but file not open\n");
}
} else {
puts("Not logging to file because level <= mFileLevel");
}
*syslogEnd = '\0';
if ( level <= mDatabaseLevel ) {
char sql[ZM_SQL_MED_BUFSIZ];
char escapedString[(strlen(syslogStart)*2)+1];
db_mutex.lock();
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
@ -539,11 +549,12 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &dbconn ) );
databaseLevel(tempDatabaseLevel);
}
db_mutex.unlock();
}
if ( level <= mSyslogLevel ) {
int priority = smSyslogPriorities[level];
//priority |= LOG_DAEMON;
syslog( priority, "%s [%s] [%s]", classString, mId.c_str(), syslogStart );
syslog( priority, "%s [%d] [%s] [%s]", classString, priority, mId.c_str(), syslogStart );
}
free(filecopy);
@ -552,7 +563,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
zmDbClose();
if ( level <= PANIC )
abort();
exit( -1 );
exit(-1);
}
}

View File

@ -58,7 +58,7 @@ public:
class Options {
public:
int mTermLevel;
int mTerminalLevel;
int mDatabaseLevel;
int mFileLevel;
int mSyslogLevel;
@ -67,8 +67,8 @@ public:
std::string mLogFile;
public:
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
mTermLevel( termLevel ),
Options( Level terminalLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
mTerminalLevel( terminalLevel ),
mDatabaseLevel( databaseLevel ),
mFileLevel( fileLevel ),
mSyslogLevel( syslogLevel ),
@ -93,7 +93,7 @@ private:
std::string mIdArgs;
Level mLevel; // Level that is currently in operation
Level mTermLevel; // Maximum level output via terminal
Level mTerminalLevel; // Maximum level output via terminal
Level mDatabaseLevel; // Maximum level output via database
Level mFileLevel; // Maximum level output via file
Level mSyslogLevel; // Maximum level output via syslog
@ -104,7 +104,7 @@ private:
std::string mLogFile;
FILE *mLogFileFP;
bool mHasTerm;
bool mHasTerminal;
bool mFlush;
private:
@ -114,10 +114,8 @@ public:
friend void logInit( const char *name, const Options &options );
friend void logTerm();
static Logger *fetch()
{
if ( !smInstance )
{
static Logger *fetch() {
if ( !smInstance ) {
smInstance = new Logger();
Options options;
smInstance->initialise( "undef", options );
@ -134,8 +132,7 @@ public:
void terminate();
private:
int limit( int level )
{
int limit( int level ) {
if ( level > DEBUG9 )
return( DEBUG9 );
if ( level < NOLOG )
@ -143,24 +140,22 @@ private:
return( level );
}
bool boolEnv( const std::string &name, bool defaultValue=false );
int intEnv( const std::string &name, bool defaultValue=0 );
std::string strEnv( const std::string &name, const std::string &defaultValue="" );
char *getTargettedEnv( const std::string &name );
bool boolEnv(const std::string &name, bool defaultValue=false);
int intEnv(const std::string &name, bool defaultValue=0);
std::string strEnv(const std::string &name, const std::string &defaultValue="");
char *getTargettedEnv(const std::string &name);
void loadEnv();
public:
const std::string &id() const
{
return( mId );
const std::string &id() const {
return mId;
}
const std::string &id( const std::string &id );
const std::string &id(const std::string &id);
Level level() const
{
return( mLevel );
Level level() const {
return mLevel;
}
Level level( Level=NOOPT );
@ -168,7 +163,7 @@ public:
return( mEffectiveLevel >= DEBUG1 );
}
Level termLevel( Level=NOOPT );
Level terminalLevel( Level=NOOPT );
Level databaseLevel( Level=NOOPT );
Level fileLevel( Level=NOOPT );
Level syslogLevel( Level=NOOPT );

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include <vector>
#include <sstream>
#include <thread>
#include "zm.h"
#include "zm_coord.h"
@ -66,6 +67,15 @@ public:
NODECT
} Function;
typedef enum {
LOCAL,
REMOTE,
FILE,
FFMPEG,
LIBVLC,
CURL,
} CameraType;
typedef enum {
ROTATE_0=1,
ROTATE_90,
@ -135,6 +145,8 @@ protected:
};
uint8_t control_state[256]; /* +88 */
char alarm_cause[256];
} SharedData;
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
@ -227,6 +239,7 @@ protected:
char name[64];
unsigned int server_id; // Id of the Server object
unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more.
CameraType type;
Function function; // What the monitor is doing
bool enabled; // Whether the monitor is enabled or asleep
unsigned int width; // Normally the same as the camera, but not if partly rotated
@ -237,7 +250,7 @@ protected:
unsigned int deinterlacing;
bool videoRecording;
int savejpegspref;
int savejpegs;
VideoWriter videowriter;
std::string encoderparams;
std::vector<EncoderParameter_t> encoderparamsvec;
@ -325,6 +338,7 @@ protected:
Image **images;
const unsigned char *privacy_bitmask;
std::thread *event_delete_thread; // Used to close events, but continue processing.
int n_linked_monitors;
MonitorLink **linked_monitors;
@ -429,22 +443,22 @@ public:
unsigned int Colours() const;
unsigned int SubpixelOrder() const;
int GetOptSaveJPEGs() const { return( savejpegspref ); }
VideoWriter GetOptVideoWriter() const { return( videowriter ); }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
int GetOptSaveJPEGs() const { return savejpegs; }
VideoWriter GetOptVideoWriter() const { return videowriter; }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return &encoderparamsvec; }
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; }
void SetVideoWriterEventId( uint32_t p_event_id ) { video_store_data->current_event = p_event_id; }
unsigned int GetPreEventCount() const { return pre_event_count; };
State GetState() const;
int GetImage( int index=-1, int scale=100 );
Snapshot *getSnapshot();
Snapshot *getSnapshot() const;
struct timeval GetTimestamp( int index=-1 ) const;
void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate();
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
int GetCaptureDelay() const { return( capture_delay ); }
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
unsigned int GetAnalysisUpdateDelay() const { return analysis_update_delay; }
int GetCaptureDelay() const { return capture_delay; }
int GetAlarmCaptureDelay() const { return alarm_capture_delay; }
unsigned int GetLastReadIndex() const;
unsigned int GetLastWriteIndex() const;
uint32_t GetLastEventId() const;
@ -452,13 +466,9 @@ public:
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
void ForceAlarmOff();
void CancelForced();
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
inline time_t getStartupTime() const {
return( shared_data->startup_time );
}
inline void setStartupTime( time_t p_time ) {
shared_data->startup_time = p_time;
}
TriggerState GetTriggerState() const { return (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL); }
inline time_t getStartupTime() const { return shared_data->startup_time; }
inline void setStartupTime( time_t p_time ) { shared_data->startup_time = p_time; }
void actionReload();
void actionEnable();
@ -471,10 +481,10 @@ public:
int actionColour( int p_colour=-1 );
int actionContrast( int p_contrast=-1 );
int PrimeCapture();
int PreCapture();
int PrimeCapture() const;
int PreCapture() const;
int Capture();
int PostCapture();
int PostCapture() const;
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
@ -492,15 +502,17 @@ public:
bool DumpSettings( char *output, bool verbose );
void DumpZoneImage( const char *zone_string=0 );
static int LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose); // Returns # of Monitors loaded, 0 on failure.
#if ZM_HAS_V4L
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
static int LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose);
#endif // ZM_HAS_V4L
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
static int LoadRemoteMonitors(const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose);
static int LoadFileMonitors(const char *file, Monitor **&monitors, Purpose purpose);
#if HAVE_LIBAVFORMAT
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
static int LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose purpose);
#endif // HAVE_LIBAVFORMAT
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
static Monitor *Load(unsigned int id, bool load_zones, Purpose purpose);
static Monitor *Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose);
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );

View File

@ -45,6 +45,7 @@ protected:
std::string username;
std::string password;
std::string auth64;
struct addrinfo *hp;
// Reworked authentication system
// First try without authentication, even if we have a username and password
@ -53,8 +54,6 @@ protected:
// subsequent requests can set the required authentication header.
bool mNeedAuth;
zm::Authenticator* mAuthenticator;
protected:
struct addrinfo *hp;
public:
RemoteCamera(

View File

@ -88,12 +88,12 @@ bool StreamBase::checkCommandQueue() {
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
//}
else {
Debug(2, "Message length is (%d)", nbytes );
Debug(2, "Message length is (%d)", nbytes );
processCommand( &msg );
return( true );
}
} else {
Error("sd is < 0");
Warning("No sd in checkCommandQueue, comms not open?");
}
return( false );
}

View File

@ -30,6 +30,7 @@
#include "zm_utils.h"
User::User() {
id = 0;
username[0] = password[0] = 0;
enabled = false;
stream = events = control = monitors = system = PERM_NONE;
@ -37,6 +38,7 @@ User::User() {
User::User( MYSQL_ROW &dbrow ) {
int index = 0;
id = atoi( dbrow[index++] );
strncpy( username, dbrow[index++], sizeof(username)-1 );
strncpy( password, dbrow[index++], sizeof(password)-1 );
enabled = (bool)atoi( dbrow[index++] );
@ -59,6 +61,7 @@ User::~User() {
}
void User::Copy( const User &u ) {
id=u.id;
strncpy( username, u.username, sizeof(username)-1 );
strncpy( password, u.password, sizeof(password)-1 );
enabled = u.enabled;
@ -94,9 +97,9 @@ User *zmLoadUser( const char *username, const char *password ) {
if ( password ) {
char safer_password[129]; // current db password size is 64
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
snprintf( sql, sizeof(sql), "select Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
} else {
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
snprintf( sql, sizeof(sql), "select Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
}
if ( mysql_query( &dbconn, sql ) ) {
@ -124,7 +127,7 @@ User *zmLoadUser( const char *username, const char *password ) {
mysql_free_result( result );
return( user );
return user;
}
// Function to validate an authentication string
@ -150,7 +153,7 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
char sql[ZM_SQL_SML_BUFSIZ] = "";
snprintf( sql, sizeof(sql), "SELECT Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds FROM Users WHERE Enabled = 1" );
snprintf( sql, sizeof(sql), "SELECT Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds FROM Users WHERE Enabled = 1" );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -171,8 +174,8 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
}
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) {
const char *user = dbrow[0];
const char *pass = dbrow[1];
const char *user = dbrow[1];
const char *pass = dbrow[2];
char auth_key[512] = "";
char auth_md5[32+1] = "";
@ -231,5 +234,5 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
#endif // HAVE_DECL_MD5
Debug(1, "No user found for auth_key %s", auth );
return( 0 );
return 0;
}

View File

@ -42,6 +42,7 @@ public:
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
protected:
int id;
char username[32+1];
char password[64+1];
bool enabled;
@ -62,6 +63,7 @@ public:
Copy(u); return *this;
}
const int Id() const { return id; }
const char *getUsername() const { return( username ); }
const char *getPassword() const { return( password ); }
bool isEnabled() const { return( enabled ); }

View File

@ -211,6 +211,7 @@ int X264MP4Writer::Open() {
int X264MP4Writer::Close() {
/* Flush all pending frames */
for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
Debug(1,"Encoding delayed frame");
x264encodeloop(true);
}
@ -220,6 +221,7 @@ int X264MP4Writer::Close() {
/* Close MP4 handle */
MP4Close(mp4h);
Debug(1,"Optimising");
/* Required for proper HTTP streaming */
MP4Optimize((path + ".incomplete").c_str(), path.c_str());
@ -228,7 +230,7 @@ int X264MP4Writer::Close() {
bOpen = false;
Debug(7, "Video closed. Total frames: %d", frame_count);
Debug(1, "Video closed. Total frames: %d", frame_count);
return 0;
}
@ -413,7 +415,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
}
if ( frame_size > 0 || bFlush ) {
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",
Debug(1, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",
frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
/* Handle the previous frame */
@ -490,7 +492,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
}
} else if ( frame_size == 0 ) {
Debug(7, "x264 encode returned zero. Delayed frames: %d",
Debug(1, "x264 encode returned zero. Delayed frames: %d",
x264_encoder_delayed_frames(x264enc));
} else {
Error("x264 encode failed: %d", frame_size);

View File

@ -721,8 +721,10 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
duration =
av_rescale_q(ipkt->pts - video_last_pts, video_in_stream->time_base,
video_out_stream->time_base);
Debug(1, "duration calc: pts(%d) - last_pts(%d) = (%d)", ipkt->pts,
video_last_pts, duration);
Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(% " PRId64 ") = (%" PRId64 ")",
ipkt->pts,
video_last_pts,
duration);
if (duration < 0) {
duration = ipkt->duration;
}

View File

@ -128,10 +128,11 @@ Zone::~Zone() {
void Zone::RecordStats( const Event *event ) {
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event stats: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
db_mutex.unlock();
} // end void Zone::RecordStats( const Event *event )
bool Zone::CheckOverloadCount() {

View File

@ -1,21 +1,21 @@
//
// ZoneMinder Analysis Daemon, $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.
//
//
/*
@ -58,18 +58,18 @@ behind.
#include "zm_monitor.h"
void Usage() {
fprintf( stderr, "zma -m <monitor_id>\n" );
fprintf( stderr, "Options:\n" );
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
fprintf( stderr, " -h, --help : This screen\n" );
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
exit( 0 );
fprintf(stderr, "zma -m <monitor_id>\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n");
fprintf(stderr, " -h, --help : This screen\n");
fprintf(stderr, " -v, --version : Report the installed version of ZoneMinder\n");
exit(0);
}
int main( int argc, char *argv[] ) {
self = argv[0];
srand( getpid() * time( 0 ) );
srand(getpid() * time(0));
int id = -1;
@ -106,39 +106,39 @@ int main( int argc, char *argv[] ) {
}
if (optind < argc) {
fprintf( stderr, "Extraneous options, " );
fprintf(stderr, "Extraneous options, ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
printf("%s ", argv[optind++]);
printf("\n");
Usage();
}
if ( id < 0 ) {
fprintf( stderr, "Bogus monitor %d\n", id );
fprintf(stderr, "Bogus monitor %d\n", id);
Usage();
exit( 0 );
exit(0);
}
char log_id_string[16];
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
snprintf(log_id_string, sizeof(log_id_string), "zma_m%d", id);
zmLoadConfig();
logInit( log_id_string );
logInit(log_id_string);
hwcaps_detect();
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
Monitor *monitor = Monitor::Load(id, true, Monitor::ANALYSIS);
if ( monitor ) {
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
Info("In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled());
zmSetDefaultHupHandler();
zmSetDefaultTermHandler();
zmSetDefaultDieHandler();
sigset_t block_set;
sigemptyset( &block_set );
sigemptyset(&block_set);
useconds_t analysis_rate = monitor->GetAnalysisRate();
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
@ -148,11 +148,11 @@ int main( int argc, char *argv[] ) {
while( !zm_terminate ) {
// Process the next image
sigprocmask( SIG_BLOCK, &block_set, 0 );
sigprocmask(SIG_BLOCK, &block_set, 0);
// Some periodic updates are required for variable capturing framerate
if ( analysis_update_delay ) {
cur_time = time( 0 );
cur_time = time(0);
if ( (unsigned int)( cur_time - last_analysis_update_time ) > analysis_update_delay ) {
analysis_rate = monitor->GetAnalysisRate();
monitor->UpdateAdaptiveSkip();
@ -161,22 +161,22 @@ int main( int argc, char *argv[] ) {
}
if ( !monitor->Analyse() ) {
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
usleep(monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE);
} else if ( analysis_rate ) {
usleep( analysis_rate );
usleep(analysis_rate);
}
if ( zm_reload ) {
monitor->Reload();
logTerm();
logInit( log_id_string );
logInit(log_id_string);
zm_reload = false;
}
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
}
sigprocmask(SIG_UNBLOCK, &block_set, 0);
} // end while ! zm_terminate
delete monitor;
} else {
fprintf( stderr, "Can't find monitor with id of %d\n", id );
fprintf(stderr, "Can't find monitor with id of %d\n", id);
}
Image::Deinitialise();
logTerm();

View File

@ -221,14 +221,6 @@ int main(int argc, char *argv[]) {
}
Info("Starting Capture version %s", ZM_VERSION);
static char sql[ZM_SQL_SML_BUFSIZ];
for ( int i = 0; i < n_monitors; i ++ ) {
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
}
zmSetDefaultTermHandler();
zmSetDefaultDieHandler();
@ -242,9 +234,15 @@ int main(int argc, char *argv[]) {
while( ! zm_terminate ) {
result = 0;
static char sql[ZM_SQL_SML_BUFSIZ];
for ( int i = 0; i < n_monitors; i ++ ) {
time_t now = (time_t)time(NULL);
monitors[i]->setStartupTime(now);
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
}
// Outer primary loop, handles connection to camera
if ( monitors[0]->PrimeCapture() < 0 ) {
@ -252,7 +250,6 @@ int main(int argc, char *argv[]) {
sleep(10);
continue;
}
static char sql[ZM_SQL_SML_BUFSIZ];
for ( int i = 0; i < n_monitors; i ++ ) {
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
@ -346,6 +343,11 @@ int main(int argc, char *argv[]) {
} // end while ! zm_terminate outer connection loop
for ( int i = 0; i < n_monitors; i++ ) {
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
delete monitors[i];
}
delete [] monitors;

View File

@ -41,7 +41,8 @@ bool ValidateAccess( User *user, int mon_id ) {
allowed = false;
}
if ( !allowed ) {
Error( "Error, insufficient privileges for requested action" );
Error( "Error, insufficient privileges for requested action user %d %s for monitor %d",
user->Id(), user->getUsername(), mon_id );
exit( -1 );
}
return( allowed );
@ -282,10 +283,10 @@ int main( int argc, const char *argv[] ) {
stream.setStreamMode( replay );
stream.setStreamQueue( connkey );
if ( monitor_id && event_time ) {
stream.setStreamStart( monitor_id, event_time );
stream.setStreamStart(monitor_id, event_time);
} else {
Debug(3, "Setting stream start to frame (%d)", frame_id);
stream.setStreamStart( event_id, frame_id );
stream.setStreamStart(event_id, frame_id);
}
if ( mode == ZMS_JPEG ) {
stream.setStreamType( EventStream::STREAM_JPEG );

View File

@ -0,0 +1,22 @@
From 634281a4204467b9a3c8a1a5febcc8dd9828e0f6 Mon Sep 17 00:00:00 2001
From: Andy Bauer <knnniggett@hotmail.com>
Date: Thu, 22 Feb 2018 08:53:50 -0600
Subject: [PATCH] don't run lintian checks to speed up build
---
pack/deb.mk | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pack/deb.mk b/pack/deb.mk
index de4a0b7..bddf9df 100644
--- a/packpack/pack/deb.mk
+++ b/packpack/pack/deb.mk
@@ -130,7 +130,7 @@ $(BUILDDIR)/$(DPKG_CHANGES): $(BUILDDIR)/$(PRODUCT)-$(VERSION)/debian \
@echo "Building Debian packages"
@echo "-------------------------------------------------------------------"
cd $(BUILDDIR)/$(PRODUCT)-$(VERSION) && \
- debuild --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \
+ debuild --no-lintian --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \
-Z$(TARBALL_COMPRESSOR) -uc -us $(SMPFLAGS)
rm -rf $(BUILDDIR)/$(PRODUCT)-$(VERSION)/
@echo "------------------------------------------------------------------"

View File

@ -0,0 +1,57 @@
From 62a98b36fd62d328956503bc9427ae128bb811af Mon Sep 17 00:00:00 2001
From: Andrew Bauer <zonexpertconsulting@outlook.com>
Date: Mon, 26 Feb 2018 10:05:02 -0600
Subject: [PATCH] fix 32bit rpm builds
---
pack/rpm.mk | 2 +-
packpack | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packpack/pack/rpm.mk b/packpack/pack/rpm.mk
index c74e942..9a6b016 100644
--- a/packpack/pack/rpm.mk
+++ b/packpack/pack/rpm.mk
@@ -124,7 +124,7 @@ package: $(BUILDDIR)/$(RPMSRC)
@echo "-------------------------------------------------------------------"
@echo "Building RPM packages"
@echo "-------------------------------------------------------------------"
- rpmbuild \
+ setarch $(ARCH) rpmbuild \
--define '_topdir $(BUILDDIR)' \
--define '_sourcedir $(BUILDDIR)' \
--define '_specdir $(BUILDDIR)' \
diff --git a/packpack/packpack b/packpack/packpack
index 6f4c80f..c329399 100755
--- a/packpack/packpack
+++ b/packpack/packpack
@@ -125,7 +125,7 @@ chmod a+x ${BUILDDIR}/userwrapper.sh
#
# Save defined configuration variables to ./env file
#
-env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \
+env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=|^ARCH=" \
> ${BUILDDIR}/env
#
diff --git a/packpack/packpack b/packpack/packpack
index c329399..6ffaa9c 100755
--- a/packpack/packpack
+++ b/packpack/packpack
@@ -19,11 +19,11 @@ DOCKER_REPO=${DOCKER_REPO:-packpack/packpack}
if [ -z "${ARCH}" ]; then
# Use uname -m instead of HOSTTYPE
case "$(uname -m)" in
- i*86) ARCH="i386" ;;
- arm*) ARCH="armhf" ;;
- x86_64) ARCH="x86_64"; ;;
- aarch64) ARCH="aarch64" ;;
- *) ARCH="${HOSTTYPE}" ;;
+ i*86) export ARCH="i386" ;;
+ arm*) export ARCH="armhf" ;;
+ x86_64) export ARCH="x86_64"; ;;
+ aarch64) export ARCH="aarch64" ;;
+ *) export ARCH="${HOSTTYPE}" ;;
esac
fi

View File

@ -102,12 +102,27 @@ commonprep () {
git clone https://github.com/packpack/packpack.git packpack
fi
# Rpm builds are broken in latest packpack master. Temporarily roll back.
git -C packpack checkout 7cf23ee
# Patch packpack
patch --dry-run --silent -f -p1 < utils/packpack/packpack-rpm.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/packpack-rpm.patch
fi
# Skip deb lintian checks to speed up the build
patch --dry-run --silent -f -p1 < utils/packpack/nolintian.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/nolintian.patch
fi
# fix 32bit rpm builds
patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/setarch.patch
fi
# The rpm specfile requires we download the tarball and manually move it into place
# Might as well do this for Debian as well, rather than git submodule init
CRUDVER="3.0.10"
@ -295,7 +310,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
execpackpack
# Steps common to Debian based distros
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
echo "Begin ${OS} ${DIST} build..."
setdebpkgname

View File

@ -1 +1 @@
1.31.37
1.31.40

View File

@ -1,7 +1,12 @@
<?php
if ($_REQUEST['entity'] == 'navBar') {
ajaxResponse(getNavBarHtml('reload'));
return;
$data = array();
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
$data['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS );
}
$data['message'] = getNavBarHtml('reload');
ajaxResponse($data);
return;
}
$statusData = array(

View File

@ -4,32 +4,42 @@
*
* Use it to configure database for ACL
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
/*
*
/**
* Using the Schema command line utility
* cake schema run create DbAcl
*
*/
class DbAclSchema extends CakeSchema {
/**
* Before event.
*
* @param array $event The event data.
* @return bool Success
*/
public function before($event = array()) {
return true;
}
/**
* After event.
*
* @param array $event The event data.
* @return void
*/
public function after($event = array()) {
}
@ -44,7 +54,11 @@ class DbAclSchema extends CakeSchema {
'alias' => array('type' => 'string', 'null' => true),
'lft' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10),
'rght' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
'idx_acos_lft_rght' => array('column' => array('lft', 'rght'), 'unique' => 0),
'idx_acos_alias' => array('column' => 'alias', 'unique' => 0)
)
);
/**
@ -58,7 +72,11 @@ class DbAclSchema extends CakeSchema {
'alias' => array('type' => 'string', 'null' => true),
'lft' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10),
'rght' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
'idx_aros_lft_rght' => array('column' => array('lft', 'rght'), 'unique' => 0),
'idx_aros_alias' => array('column' => 'alias', 'unique' => 0)
)
);
/**
@ -73,7 +91,11 @@ class DbAclSchema extends CakeSchema {
'_read' => array('type' => 'string', 'null' => false, 'default' => '0', 'length' => 2),
'_update' => array('type' => 'string', 'null' => false, 'default' => '0', 'length' => 2),
'_delete' => array('type' => 'string', 'null' => false, 'default' => '0', 'length' => 2),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'ARO_ACO_KEY' => array('column' => array('aro_id', 'aco_id'), 'unique' => 1))
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
'ARO_ACO_KEY' => array('column' => array('aro_id', 'aco_id'), 'unique' => 1),
'idx_aco_id' => array('column' => 'aco_id', 'unique' => 0)
)
);
}

View File

@ -1,11 +1,11 @@
# $Id$
#
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (http://www.opensource.org/licenses/mit-license.php)
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE acos (
id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -38,4 +38,15 @@ CREATE TABLE aros (
lft INTEGER(10) DEFAULT NULL,
rght INTEGER(10) DEFAULT NULL,
PRIMARY KEY (id)
);
);
/* this indexes will improve acl perfomance */
CREATE INDEX idx_acos_lft_rght ON `acos` (`lft`, `rght`);
CREATE INDEX idx_acos_alias ON `acos` (`alias`);
CREATE INDEX idx_aros_lft_rght ON `aros` (`lft`, `rght`);
CREATE INDEX idx_aros_alias ON `aros` (`alias`);
CREATE INDEX idx_aco_id ON `aros_acos` (`aco_id`);

View File

@ -4,22 +4,21 @@
*
* Use it to configure database for i18n
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
/**
*
* Using the Schema command line utility
*
* Use it to configure database for i18n
@ -28,15 +27,37 @@
*/
class I18nSchema extends CakeSchema {
/**
* The name property
*
* @var string
*/
public $name = 'i18n';
/**
* Before callback.
*
* @param array $event Schema object properties
* @return bool Should process continue
*/
public function before($event = array()) {
return true;
}
/**
* After callback.
*
* @param array $event Schema object properties
* @return void
*/
public function after($event = array()) {
}
/**
* The i18n table definition
*
* @var array
*/
public $i18n = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'),
'locale' => array('type' => 'string', 'null' => false, 'length' => 6, 'key' => 'index'),

View File

@ -1,11 +1,11 @@
# $Id$
#
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (http://www.opensource.org/licenses/mit-license.php)
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE i18n (
id int(10) NOT NULL auto_increment,

View File

@ -1,13 +1,13 @@
# $Id$
#
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
# 1785 E. Sahara Avenue, Suite 490-204
# Las Vegas, Nevada 89104
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (http://www.opensource.org/licenses/mit-license.php)
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE cake_sessions (
id varchar(255) NOT NULL default '',

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