Merge branch 'storageareas' into fugro

pull/2077/head
Isaac Connor 2018-01-10 15:08:28 -05:00
commit 5792021ee3
45 changed files with 1365 additions and 478 deletions

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git
branch = 3.0
[submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"]
path = web/api/app/Plugin/CakePHP-Enum-Behavior
url = https://github.com/asper/CakePHP-Enum-Behavior.git

View File

@ -647,13 +647,13 @@ if(NOT ZM_NO_LIBVLC)
endif(LIBVLC_LIBRARIES)
endif(NOT ZM_NO_LIBVLC)
find_package(Boost 1.36.0)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
set(CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIRS}")
list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
endif()
#find_package(Boost 1.36.0)
#if(Boost_FOUND)
#include_directories(${Boost_INCLUDE_DIRS})
##set(CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIRS}")
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
#endif()
#
# *** END OF LIBRARY CHECKS ***
# Check for gnutls or crypto

View File

@ -209,11 +209,62 @@ CREATE TABLE `Events` (
`StateId` int(10) unsigned NOT NULL,
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`Id`,`MonitorId`),
KEY `MonitorId` (`MonitorId`),
KEY `StartTime` (`StartTime`),
KEY `Frames` (`Frames`),
KEY `Archived` (`Archived`)
PRIMARY KEY (`Id`),
KEY `Events_MonitorId_idx` (`MonitorId`),
KEY `Events_StorageId_idx` (`StorageId`),
KEY `Events_StartTime_idx` (`StartTime`),
KEY `Events_EndTime_DiskSpace` (`EndTime`,`DiskSpace`)
) ENGINE=@ZM_MYSQL_ENGINE@;
DROP TABLE IF EXISTS `Events_Hour`;
CREATE TABLE `Events_Hour` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Hour_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
DROP TABLE IF EXISTS `Events_Day`;
CREATE TABLE `Events_Day` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Day_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
DROP TABLE IF EXISTS `Events_Week`;
CREATE TABLE `Events_Week` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Week_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
DROP TABLE IF EXISTS `Events_Month`;
CREATE TABLE `Events_Month` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
DROP TABLE IF EXISTS `Events_Archived`;
CREATE TABLE `Events_Archived` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_idx` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@;
--
@ -285,8 +336,8 @@ CREATE TABLE `Groups_Monitors` (
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
CREATE INDEX `Groups_Monitors_GroupId_idx` ON `Groups` (`GroupId`);
CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups` (`MonitorId`);
CREATE INDEX `Groups_Monitors_GroupId_idx` ON `Groups_Monitors` (`GroupId`);
CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups_Monitors` (`MonitorId`);
--
-- Table structure for table `Logs`
@ -501,6 +552,9 @@ CREATE TABLE `Servers` (
`FreeMem` bigint unsigned default null,
`TotalSwap` bigint unsigned default null,
`FreeSwap` bigint unsigned default null,
`zmstats.pl` BOOLEAN NOT NULL DEFAULT FALSE,
`zmaudit.pl` BOOLEAN NOT NULL DEFAULT FALSE,
`zmtrigger.pl` BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@ -639,14 +693,15 @@ CREATE TABLE `Storage` (
`Name` varchar(64) NOT NULL default '',
`Type` enum('local','s3fs') NOT NULL default 'local',
`DiskSpace` bigint unsigned default NULL,
`Scheme enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`ServerId` int(10) unsigned,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Create a default storage location
--
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default', 'local', NULL );
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default', 'local', NULL, 'Medium', 0 );
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
@ -668,7 +723,25 @@ insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edi
--
-- Add a sample filter to purge the oldest 100 events when the disk is 95% full
--
insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0,0,0,0,0,0,'',1,0,1,0);
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`Query` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
`AutoEmail` tinyint(3) unsigned NOT NULL default '0',
`AutoMessage` tinyint(3) unsigned NOT NULL default '0',
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
`AutoExecuteCmd` tinytext,
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
`AutoMove` tinyint(3) unsigned NOT NULL default '0',
`AutoMoveTo` smallint(5) unsigned NOT NULL default 0,
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
`Background` tinyint(1) unsigned NOT NULL default '0',
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0/*AutoArchive*/,0/*AutoVideo*/,0/*AutoUpload*/,0/*AutoEmail*/,0/*AutoMessage*/,0/*AutoExecute*/,'',1/*AutoDelete*/,0/*AutoMove*/,0/*MoveTo*/,0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,1,1,0);
--
-- Add in some sample control protocol definitions

23
db/zm_update-1.31.18.sql Normal file
View File

@ -0,0 +1,23 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'
AND column_name = 'ServerId'
) > 0,
"SELECT 'Column ServerId already exists in Storage'",
"ALTER TABLE Storage ADD `ServerId` int(10) unsigned AFTER `Scheme`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM Filters WHERE Name = 'Update DiskSpace'
AND Query = '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}'
) > 0,
"SELECT 'Update Disk Space Filter already exists.'",
"INSERT INTO Filters (Name,Query,UpdateDiskSpace,Background) values ('Update DiskSpace','{\"terms\":[{\"attr\":\"DiskSpace\",\"op\":\"IS\",\"val\":\"NULL\"}]}',1,1)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

123
db/zm_update-1.31.19.sql Normal file
View File

@ -0,0 +1,123 @@
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 = DiskSpace + space where Id = StorageId;
end;
//
delimiter ;
drop trigger if exists event_update_trigger;
delimiter //
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;
end;
//
delimiter ;
drop trigger if exists event_insert_trigger;
delimiter //
/*
create trigger event_insert_trigger
after insert
on Events
for each row
begin
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
end;
*/
//
delimiter ;
drop trigger if exists event_delete_trigger;
delimiter //
create trigger event_delete_trigger
before delete
on Events
for each row
begin
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
end;
//
delimiter ;
set @exist := (select count(*) from information_schema.statistics where table_name = 'Events' and index_name = 'Archived' and table_schema = database());
set @sqlstmt := if( @exist > 0, 'DROP INDEX Archived ON Events', "SELECT 'Archived INDEX is already removed.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @exist := (select count(*) from information_schema.statistics where table_name = 'Events' and index_name = 'Frames' and table_schema = database());
set @sqlstmt := if( @exist > 0, 'DROP INDEX Frames ON Events', "SELECT 'Frames INDEX is already removed.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @exist := (select count(*) from information_schema.statistics where table_name = 'Events' and index_name = 'Events_StorageId_idx' and table_schema = database());
set @sqlstmt := if( @exist > 0, "SELECT 'Index Events_StorageId_idx already exists.'", 'CREATE INDEX Events_StorageId_idx on Events (StorageId)');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @exist := (select count(*) from information_schema.statistics where table_name = 'Events' and index_name = 'Events_EndTime_DiskSpace_idx' and table_schema = database());
set @sqlstmt := if( @exist > 0, "SELECT 'Index Events_EndTime_DiskSpace_idx already exists.'", 'CREATE INDEX Events_EndTime_DiskSpace_idx on Events (EndTime, DiskSpace)');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
UPDATE Storage SET DiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=Storage.Id);

View File

@ -15,6 +15,3 @@ SET @s = (SELECT IF(
PREPARE stmt FROM @s;
EXECUTE stmt;
INSERT INTO `Controls` VALUES (28,'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);

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

@ -0,0 +1,275 @@
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Events_Hour'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Events_Hour table exists'",
"
CREATE TABLE `Events_Hour` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Hour_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
);
"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Events_Day'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Events_Day table exists'",
"
CREATE TABLE `Events_Day` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Day_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
);
"));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Events_Week'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Events_Week table exists'",
"
CREATE TABLE `Events_Week` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Week_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
);
"));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Events_Month'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Events_Month table exists'",
"
CREATE TABLE `Events_Month` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_StartTime_idx` (`MonitorId`,`StartTime`)
);
"));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Events_Archived'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Events_Archived table exists'",
"
CREATE TABLE `Events_Archived` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_idx` (`MonitorId`)
);
"));
PREPARE stmt FROM @s;
EXECUTE stmt;
drop trigger if exists event_update_trigger;
delimiter //
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 = ArchivedEvents+1, ArchivedEventDiskSpace = ArchivedEventDiskSpace + NEW.DiskSpace WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents =ArchivedEvents-1, ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace 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 = ArchivedEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END IF;
END IF;
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Monitors SET TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
DROP TRIGGER IF EXISTS event_insert_trigger;
delimiter //
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);
end;
//
delimiter ;
drop trigger if exists event_delete_trigger;
delimiter //
CREATE TRIGGER event_delete_trigger BEFORE DELETe on Events
FOR EACH ROW
BEGIN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
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 = ArchivedEventDiskSpace - OLD.DiskSpace,
TotalEvents = TotalEvents - 1,
TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = TotalEvents-1,
TotalEventDiskSpace=TotalEventDiskSpace-OLD.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Servers'
AND column_name = 'zmstats.pl'
) > 0,
"SELECT 'Column zmstats.pl already exists in Servers'",
"ALTER TABLE Servers ADD `zmstats.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `FreeSwap`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Servers'
AND column_name = 'zmaudit.pl'
) > 0,
"SELECT 'Column zmaudit.pl already exists in Servers'",
"ALTER TABLE Servers ADD `zmaudit.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmstats.pl`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Servers'
AND column_name = 'zmtrigger.pl'
) > 0,
"SELECT 'Column zmtrigger.pl already exists in Servers'",
"ALTER TABLE Servers ADD `zmtrigger.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmaudit.pl`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
UPDATE Monitors INNER JOIN (
SELECT MonitorId,
COUNT(Id) AS TotalEvents,
SUM(DiskSpace) AS TotalEventDiskSpace,
SUM(IF(Archived,1,0)) AS ArchivedEvents,
SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
FROM Events GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.TotalEvents = E.TotalEvents,
Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
Monitors.ArchivedEvents = E.ArchivedEvents,
Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
Monitors.HourEvents = E.HourEvents,
Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
Monitors.DayEvents = E.DayEvents,
Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
Monitors.WeekEvents = E.WeekEvents,
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
Monitors.MonthEvents = E.MonthEvents,
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;

View File

@ -31,9 +31,9 @@ if [ "$1" = "configure" ]; then
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
# This creates the user.
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
echo "grant lock tables, alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
echo "grant lock tables, alter,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

View File

@ -4,7 +4,8 @@
[Unit]
Description=ZoneMinder CCTV recording and surveillance system
After=network.target mysql.service
Requires=mysql.service
# Remarked out so that it will start ZM on machines that don't have mysql installed
#Requires=mysql.service
[Service]
#User=www-data
@ -13,7 +14,10 @@ ExecStart=/usr/bin/zmpkg.pl start
ExecReload=/usr/bin/zmpkg.pl restart
ExecStop=/usr/bin/zmpkg.pl stop
PIDFile=/var/run/zm/zm.pid
Restart=on-abnormal
Restart=always
RestartSec=10
Environment=TZ=:/etc/localtime
TimeoutSec=600
[Install]
WantedBy=multi-user.target

View File

@ -33,9 +33,9 @@ if [ "$1" = "configure" ]; then
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
# This creates the user.
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
echo "grant lock tables,alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
echo "grant lock tables,alter,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

View File

@ -17,6 +17,7 @@ PIDFile=/var/run/zm/zm.pid
Restart=always
RestartSec=10
Environment=TZ=:/etc/localtime
TimeoutSec=600
[Install]
WantedBy=multi-user.target

View File

@ -14,6 +14,7 @@ configure_file(zmtrigger.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" @ONLY)
configure_file(zmupdate.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" @ONLY)
configure_file(zmvideo.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" @ONLY)
configure_file(zmwatch.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" @ONLY)
configure_file(zmstats.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" @ONLY)
configure_file(zmcamtool.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" @ONLY)
configure_file(zmsystemctl.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" @ONLY)
configure_file(zmtelemetry.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" @ONLY)
@ -33,7 +34,7 @@ FOREACH(PERLSCRIPT ${perlscripts})
ENDFOREACH(PERLSCRIPT ${perlscripts})
# Install the perl scripts
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if(NOT ZM_NO_X10)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
endif(NOT ZM_NO_X10)

View File

@ -2399,6 +2399,17 @@ our @options = (
type => $types{integer},
category => 'system',
},
{
name => 'ZM_STATS_UPDATE_INTERVAL',
default => '60',
description => 'How often to update the database statistics',
help => q`
The zmstats daemon performs various db queries that may take
a long time in the background.
`,
type => $types{integer},
category => 'system',
},
{
name => 'ZM_WATCH_CHECK_INTERVAL',
default => '10',

View File

@ -200,6 +200,32 @@ sub RelativePath {
return $$event{RelativePath};
}
sub LinkPath {
my $event = shift;
if ( @_ ) {
$$event{LinkPath} = $_[0];
}
if ( ! $$event{LinkPath} ) {
if ( $$event{Scheme} eq 'Deep' ) {
if ( $event->Time() ) {
$$event{LinkPath} = join('/',
$event->{MonitorId},
strftime( '%y/%m/%d',
localtime($event->Time())
),
'.'.$$event{Id}
);
} else {
Error("Event $$event{Id} has no value for Time(), unable to determine link path");
$$event{LinkPath} = '';
}
} # end if Scheme
} # end if ! Path
return $$event{LinkPath};
} # end sub LinkPath
sub GenerateVideo {
my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_;
@ -292,10 +318,10 @@ sub delete {
my $event = $_[0];
if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) {
my ( $caller, undef, $line ) = caller;
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime} from $caller:$line\n" );
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n" );
return;
}
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime}\n" );
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" );
$ZoneMinder::Database::dbh->ping();
# Do it individually to avoid locking up the table for new events
my $sql = 'DELETE FROM Events WHERE Id=?';
@ -337,50 +363,25 @@ sub delete_files {
return;
}
chdir( $storage_path );
if ( ! $$event{MonitorId} ) {
Error("No monitor id assigned to event $$event{Id}");
return;
}
my $event_path = $event->Path();
Debug("Deleting files for Event $$event{Id} from $event_path.");
if ( $event_path ) {
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $command = "/bin/rm -rf $event_path";
ZoneMinder::General::executeShellCommand( $command );
}
if ( $$event{Scheme} eq 'Deep' ) {
if ( ! $$event{MonitorId} ) {
Error("No monitor id assigned to event $$event{Id}");
return;
my $link_path = $event->LinkPath();
Debug("Deleting files for Event $$event{Id} from $storage_path/$link_path.");
if ( $link_path ) {
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
unlink( $storage_path.'/'.$link_path ) or Error( "Unable to unlink '$storage_path/$link_path': $!" );
}
Debug("Deleting files for Event $$event{Id} from $storage_path.");
my $link_path = $$event{MonitorId}.'/*/*/*/.'.$$event{Id};
#Debug( "LP1:$link_path" );
my @links = glob($link_path);
#Debug( "L:".$links[0].": $!" );
if ( @links ) {
( $link_path ) = ( $links[0] =~ /^(.*)$/ ); # De-taint
#Debug( "LP2:$link_path" );
( my $day_path = $link_path ) =~ s/\.\d+//;
#Debug( "DP:$day_path" );
my $event_path = $day_path.readlink( $link_path );
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
#Debug( "EP:$event_path" );
my $command = "/bin/rm -rf $event_path";
#Debug( "C:$command" );
ZoneMinder::General::executeShellCommand( $command );
unlink( $link_path ) or Error( "Unable to unlink '$link_path': $!" );
my @path_parts = split( /\//, $event_path );
for ( my $i = int(@path_parts)-2; $i >= 1; $i-- ) {
my $delete_path = join( '/', @path_parts[0..$i] );
#Debug( "DP$i:$delete_path" );
my @has_files = glob( join('/', $storage_path,$delete_path,'*' ) );
#Debug( "HF1:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
@has_files = glob( join('/', $storage_path, $delete_path, '.[0-9]*' ) );
#Debug( "HF2:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
my $command = "/bin/rm -rf $storage_path/$delete_path";
ZoneMinder::General::executeShellCommand( $command );
}
} # end if links
} else {
my $command = '/bin/rm -rf '. $storage_path . '/'. $event->RelativePath();
ZoneMinder::General::executeShellCommand( $command );
}
} # end sub delete_files
@ -456,10 +457,11 @@ sub MoveTo {
if ( @$err ) {
for my $diag (@$err) {
my ($file, $message) = %$diag;
next if $message eq 'File exists';
if ($file eq '') {
$error .= "general error: $message\n";
} else {
$error .= "problem unlinking $file: $message\n";
$error .= "problem making $file: $message\n";
}
}
}

View File

@ -133,24 +133,25 @@ sub Sql {
my $self = $_[0];
if ( ! $$self{Sql} ) {
my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} );
my $sql = "SELECT E.*,
my $sql = 'SELECT E.*,
unix_timestamp(E.StartTime) as Time,
M.Name as MonitorName,
M.DefaultRate,
M.DefaultScale
FROM Events as E
INNER JOIN Monitors as M on M.Id = E.MonitorId
";
INNER JOIN Storage as S on S.Id = E.StorageId
';
$self->{Sql} = '';
if ( $filter_expr->{terms} ) {
foreach my $term ( @{$filter_expr->{terms}} ) {
if ( exists($term->{cnj}) ) {
$self->{Sql} .= ' '.$term->{cnj}." ";
$self->{Sql} .= ' '.$term->{cnj}.' ';
}
if ( exists($term->{obr}) ) {
$self->{Sql} .= ' '.str_repeat( "(", $term->{obr} )." ";
$self->{Sql} .= ' '.str_repeat( '(', $term->{obr} ).' ';
}
my $value = $term->{val};
my @value_list;
@ -159,7 +160,7 @@ sub Sql {
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= 'M.'.$temp_attr_name;
} elsif ( $term->{attr} =~ /^Server/ ) {
$self->{Sql} .= 'M.'.$term->{attr};
$self->{Sql} .= 'S.'.$term->{attr};
# StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) {
@ -171,9 +172,9 @@ sub Sql {
} elsif ( $term->{attr} eq 'StartDate' ) {
$self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'Time' ) {
$self->{Sql} .= "extract( hour_second from E.StartTime )";
$self->{Sql} .= 'extract( hour_second from E.StartTime )';
} elsif ( $term->{attr} eq 'Weekday' ) {
$self->{Sql} .= "weekday( E.StartTime )";
$self->{Sql} .= 'weekday( E.StartTime )';
# EndTIme options
} elsif ( $term->{attr} eq 'EndDateTime' ) {
@ -181,7 +182,7 @@ sub Sql {
} elsif ( $term->{attr} eq 'EndDate' ) {
$self->{Sql} .= 'to_days( E.EndTime )';
} elsif ( $term->{attr} eq 'EndTime' ) {
$self->{Sql} .= "extract( hour_second from E.EndTime )";
$self->{Sql} .= 'extract( hour_second from E.EndTime )';
} elsif ( $term->{attr} eq 'EndWeekday' ) {
$self->{Sql} .= "weekday( E.EndTime )";
@ -212,6 +213,8 @@ sub Sql {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server( $ZoneMinder::Config::Config{ZM_SERVER_ID} );
} elsif ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = "'$temp_value'";
# This gets used later, I forget for what

View File

@ -172,7 +172,6 @@ MAIN: while( $loop ) {
foreach my $Storage (
ZoneMinder::Storage->find( ($Config{ZM_SERVER_ID} ? ( ServerId => $Config{ZM_SERVER_ID} ) : () ) ),
new ZoneMinder::Storage(),
) {
Debug('Checking events in ' . $Storage->Path() );
if ( ! chdir( $Storage->Path() ) ) {
@ -238,8 +237,13 @@ MAIN: while( $loop ) {
} elsif ( $$Storage{Scheme} eq 'Medium' ) {
foreach my $event_dir ( glob("$monitor_dir/*/*") ) {
next if ! -d $event_dir;
my $Event = $fs_events->{$event} = new ZoneMinder::Event();
$$Event{Id} = $event;
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d\+)$/;
if ( ! $event_id ) {
Debug("Unable to parse date/event_id from $event_dir");
next;
}
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id;
$$Event{Path} = $event_dir;
$Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() );
@ -357,8 +361,18 @@ Debug("Event $db_event is not in fs.");
}
next;
}
if ( ! $Event->EndTime() ) {
Debug("Event $$Event{Id} has no end time. deleting it.");
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
if ( confirm() ) {
$Event->delete();
$cleaned = 1;
}
next;
}
}
if ( $Event->check_for_in_filesystem() ) {
Debug('Database events apparently exists at ' . $Event->Path() );
Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() );
} else {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem' );
@ -588,6 +602,26 @@ Debug("Event $db_event is not in fs.");
} # end if ZM_LOG_DATABASE_LIMIT
$loop = $continuous;
my $eventcounts_sql = q`
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)
`;
my $eventcounts_sth = $dbh->prepare_cached( $eventcounts_sql );
$eventcounts_sth->execute();
$eventcounts_sth->finish();
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous;
};

View File

@ -69,8 +69,6 @@ use POSIX;
use Socket;
use IO::Handle;
use Time::HiRes qw(usleep);
use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad;
use autouse 'Pod::Usage'=>qw(pod2usage);
#use Data::Dumper;
@ -101,6 +99,7 @@ my @daemons = (
'zmx10.pl',
'zmwatch.pl',
'zmupdate.pl',
'zmstats.pl',
'zmtrack.pl',
'zmtelemetry.pl'
);
@ -149,8 +148,11 @@ socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" );
my $saddr = sockaddr_un( SOCK_FILE );
my $server_up = connect( CLIENT, $saddr );
if ( ! $server_up ) {
if ( $Config{ZM_SERVER_ID} ) {
use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad;
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr());
@ -234,6 +236,7 @@ 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
our %cmd_hash;
@ -281,14 +284,29 @@ sub run {
my $win = $rin;
my $ein = $win;
my $timeout = 1;
my $Server = undef;
my $secs_count = 0;
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} );
}
while( 1 ) {
if ( $Config{ZM_SERVER_ID} ) {
$dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load();
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
if ( $secs_count % 60 ) {
$dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load();
dPrint( ZoneMinder::Logger::INFO, '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());
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
}
}
$secs_count += 1;
}
my $nfound = select( my $rout = $rin, undef, undef, $timeout );
if ( $nfound > 0 ) {
@ -296,12 +314,16 @@ sub run {
my $paddr = accept( CLIENT, SERVER );
my $message = <CLIENT>;
next if ( !$message );
next if !$message;
my ( $command, $daemon, @args ) = split( /;/, $message );
if ( $command eq 'start' ) {
start( $daemon, @args );
if ( $Server and exists $$Server{$daemon} and ! $$Server{$daemon} ) {
Debug("Not running $daemon because it is turned off for this server.");
} else {
start( $daemon, @args );
}
} elsif ( $command eq 'stop' ) {
stop( $daemon, @args );
} elsif ( $command eq 'restart' ) {
@ -505,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++ > 50 ) {
if ( $count++ > 100 ) {
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"
@ -526,7 +548,6 @@ sub _stop {
my $pid = send_stop( $final, $process );
return if ! $pid;
delete( $cmd_hash{$$process{command}} );
kill_until_dead( $process );
}
@ -578,7 +599,8 @@ sub reload {
sub logrot {
logReinit();
foreach my $process ( values( %pid_hash ) ) {
if ( $process->{pid} && $process->{command} =~ /^zm.*\.pl/ ) {
if ( $process->{pid} ) {
# && $process->{command} =~ /^zm.*\.pl/ ) {
kill( 'HUP', $process->{pid} );
}
}

View File

@ -219,24 +219,18 @@ sub getFilters {
) ORDER BY Name';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable to prepare '$sql': ".$dbh->errstr() );
my $res;
if ( $filter_name ) {
$res = $sth->execute( $filter_name )
or Fatal( "Unable to execute '$sql': ".$sth->errstr() );
} else {
$res = $sth->execute()
or Fatal( "Unable to execute '$sql': ".$sth->errstr() );
}
my $res = $sth->execute( @sql_values )
or Fatal( "Unable to execute '$sql': ".$sth->errstr() );
FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter );
Debug( "Found filter '$db_filter->{Name}'\n" );
my $sql = $filter->Sql();
my $filter_sql = $filter->Sql();
if ( ! $sql ) {
if ( ! $filter_sql ) {
Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
push( @filters, $filter );
push @filters, $filter;
}
$sth->finish();
if ( ! @filters ) {
@ -245,7 +239,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
Debug( "Got " . @filters . " filters" );
}
return( @filters );
return @filters;
} # end sub getFilters
sub checkFilter {

173
scripts/zmstats.pl.in Normal file
View File

@ -0,0 +1,173 @@
#!/usr/bin/perl -wT
#
# ==========================================================================
#
# ZoneMinder WatchDog Script, $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.
#
# ==========================================================================
=head1 NAME
zmwatch.pl - ZoneMinder Stats Updating Script
=head1 SYNOPSIS
zmstats.pl
=head1 DESCRIPTION
This does background updating various stats in the db like event counts, diskspace, etc.
=cut
use strict;
use bytes;
# ==========================================================================
#
# These are the elements you can edit to suit your installation
#
# ==========================================================================
use constant START_DELAY => 30; # To give everything else time to start
# ==========================================================================
#
# Don't change anything below here
#
# ==========================================================================
@EXTRA_PERL_LIB@
use ZoneMinder;
use ZoneMinder::Storage;
use POSIX;
use DBI;
use autouse 'Data::Dumper'=>qw(Dumper);
$| = 1;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
logSetSignal();
Info( "Stats Daemon starting in ".START_DELAY." seconds\n" );
sleep( START_DELAY );
my $dbh = zmDbConnect();
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $eventcounts_sql = q`
UPDATE Monitors INNER JOIN (
SELECT MonitorId,
COUNT(Id) AS TotalEvents,
SUM(DiskSpace) AS TotalEventDiskSpace,
SUM(IF(Archived,1,0)) AS ArchivedEvents,
SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
FROM Events GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.TotalEvents = E.TotalEvents,
Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
Monitors.ArchivedEvents = E.ArchivedEvents,
Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
Monitors.HourEvents = E.HourEvents,
Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
Monitors.DayEvents = E.DayEvents,
Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
Monitors.WeekEvents = E.WeekEvents,
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
Monitors.MonthEvents = E.MonthEvents,
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace
`;
my $eventcounts_hour_sql = q`
UPDATE Monitors INNER JOIN (
SELECT MonitorId, COUNT(*) AS HourEvents, SUM(DiskSpace) AS HourEventDiskSpace
FROM Events_Hour GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.HourEvents = E.HourEvents,
Monitors.HourEventDiskSpace = E.HourEventDiskSpace
`;
my $eventcounts_day_sql = q`
UPDATE Monitors INNER JOIN (
SELECT MonitorId, COUNT(*) AS DayEvents, SUM(DiskSpace) AS DayEventDiskSpace
FROM Events_Day GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.DayEvents = E.DayEvents,
Monitors.DayEventDiskSpace = E.DayEventDiskSpace
`;
my $eventcounts_week_sql = q`
UPDATE Monitors INNER JOIN (
SELECT MonitorId, COUNT(*) AS WeekEvents, SUM(DiskSpace) AS WeekEventDiskSpace
FROM Events_Week GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.WeekEvents = E.WeekEvents,
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace
`;
my $eventcounts_month_sql = q`
UPDATE Monitors INNER JOIN (
SELECT MonitorId, COUNT(*) AS MonthEvents, SUM(DiskSpace) AS MonthEventDiskSpace
FROM Events_Month GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.MonthEvents = E.MonthEvents,
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace
`;
my $eventcounts_sth = $dbh->prepare_cached( $eventcounts_sql );
my $eventcounts_hour_sth = $dbh->prepare_cached( $eventcounts_hour_sql );
my $eventcounts_day_sth = $dbh->prepare_cached( $eventcounts_day_sql );
my $eventcounts_week_sth = $dbh->prepare_cached( $eventcounts_week_sql );
my $eventcounts_month_sth = $dbh->prepare_cached( $eventcounts_month_sql );
while( 1 ) {
$dbh->ping();
$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)');
$eventcounts_hour_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
$eventcounts_day_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
$eventcounts_week_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
$eventcounts_month_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
sleep( $Config{ZM_STATS_CHECK_INTERVAL} );
} # end while (1)
Info( "Stats Daemon exiting\n" );
exit();
1;
__END__

View File

@ -932,17 +932,44 @@ sub patchDB {
my $dbh = shift;
my $version = shift;
my $file = ( $updateDir ? $updateDir : $Config{ZM_PATH_DATA}.'/db' ) . "/zm_update-$version.sql";
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
my $command = 'mysql';
if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) {
$command .= ' -S'.$portOrSocket;
} else {
$command .= ' -h'.$host.' -P'.$portOrSocket;
}
} else {
$command .= ' -h'.$host;
}
if ( $dbUser ) {
$command .= ' -u'.$dbUser;
$command .= ' -p"'.$dbPass.'"' if $dbPass;
}
$command .= ' '.$Config{ZM_DB_NAME}.' < ';
if ( $updateDir ) {
$command .= $updateDir;
} else {
$command .= $Config{ZM_PATH_DATA}.'/db';
}
$command .= '/zm_update-'.$version.'.sql';
open( my $fh, '<', $file ) or die "Unable to open $file $!";
$/ = undef;
my $sql = <$fh>;
close $fh;
$dbh->do($sql) or die $dbh->errstr();
print( "Executing '$command'\n" ) if ( logDebugging() );
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
chomp( $output );
print( "Output: $output\n" );
}
if ( $status ) {
die( "Command '$command' exited with status: $status\n" );
}
print( "\nDatabase successfully upgraded to version $version.\n" );
$sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'";
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
}
sub migratePaths {

View File

@ -79,31 +79,12 @@ my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'S
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $eventcounts_sql = q`
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)
WHERE Id=?`;
my $eventcounts_sth = $dbh->prepare_cached( $eventcounts_sql );
while( 1 ) {
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) {
$eventcounts_sth->execute( $$monitor{Id} ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
my $now = time();
next if $monitor->{Function} eq 'None';
my $restart = 0;
@ -196,25 +177,13 @@ while( 1 ) {
} # end if check analysis daemon
# Prevent open handles building up if we have connect to shared memory
zmMemInvalidate( $monitor ); # Close our file handle to the zmc process we are about to end
} # end foreach monitor
$eventcounts_sth->finish();
my $diskspace_sql = 'UPDATE Storage SET DiskSpace =(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL)';
my $diskspace_sth = $dbh->prepare_cached( $diskspace_sql )
or Fatal( "Can't prepare '$diskspace_sql': ".$dbh->errstr() );
foreach my $Storage ( ZoneMinder::Storage->find() ) {
Debug("Updating disk space for $$Storage{Name} was $$Storage{DiskSpace}");
$diskspace_sth->execute( $$Storage{Id} ) or Error( "Can't execute: ".$diskspace_sth->errstr() );
$Storage->load();
Debug("Updated disk space for $$Storage{Name} to $$Storage{DiskSpace}");
}
$diskspace_sth->finish();
sleep( $Config{ZM_WATCH_CHECK_INTERVAL} );
} # end while (1)
Info( "Watchdog exiting\n" );
exit();
1;
__END__

View File

@ -77,10 +77,24 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
if ( ( av_packet->stream_index == stream_id) ) {
//&& ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
frames_to_keep --;
}
}
// Make sure we start on a keyframe
for ( ; it != pktQueue.rend(); ++it ) {
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
Debug(4, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
break;
}
}
if ( frames_to_keep ) {
Debug(3, "Hit end of queue, still need (%d) video keyframes", frames_to_keep );
}

View File

@ -313,6 +313,7 @@ bool VideoStore::open() {
if (ret < 0) {
Error("Could not open out file '%s': %s\n", filename,
av_make_error_string(ret).c_str());
return false;
}
}
@ -346,68 +347,83 @@ bool VideoStore::open() {
}
VideoStore::~VideoStore() {
if (audio_out_codec) {
// The codec queues data. We need to send a flush command and out
// whatever we get. Failures are not fatal.
AVPacket pkt;
av_init_packet(&pkt);
while (1) {
if ( oc->pb ) {
if (audio_out_codec) {
// The codec queues data. We need to send a flush command and out
// whatever we get. Failures are not fatal.
AVPacket pkt;
av_init_packet(&pkt);
while (1) {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
// Put encoder into flushing mode
avcodec_send_frame(audio_out_ctx, NULL);
ret = avcodec_receive_packet(audio_out_ctx, &pkt);
if (ret < 0) {
if (AVERROR_EOF != ret) {
Error("ERror encoding audio while flushing (%d) (%s)", ret,
// Put encoder into flushing mode
avcodec_send_frame(audio_out_ctx, NULL);
ret = avcodec_receive_packet(audio_out_ctx, &pkt);
if (ret < 0) {
if (AVERROR_EOF != ret) {
Error("ERror encoding audio while flushing (%d) (%s)", ret,
av_err2str(ret));
}
break;
}
break;
}
#else
int got_packet = 0;
ret =
int got_packet = 0;
ret =
avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet);
if (ret < 0) {
Error("ERror encoding audio while flushing (%d) (%s)", ret,
if (ret < 0) {
Error("ERror encoding audio while flushing (%d) (%s)", ret,
av_err2str(ret));
break;
}
Debug(1, "Have audio encoder, need to flush it's out");
if (!got_packet) {
break;
}
break;
}
Debug(1, "Have audio encoder, need to flush it's out");
if (!got_packet) {
break;
}
#endif
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
pkt.dts, pkt.duration);
pkt.pts = audio_next_pts;
pkt.dts = audio_next_dts;
pkt.pts = audio_next_pts;
pkt.dts = audio_next_dts;
if (pkt.duration > 0)
pkt.duration =
if (pkt.duration > 0)
pkt.duration =
av_rescale_q(pkt.duration, audio_out_ctx->time_base,
audio_out_stream->time_base);
audio_next_pts += pkt.duration;
audio_next_dts += pkt.duration;
audio_out_stream->time_base);
audio_next_pts += pkt.duration;
audio_next_dts += pkt.duration;
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
pkt.dts, pkt.duration);
pkt.stream_index = audio_out_stream->index;
av_interleaved_write_frame(oc, &pkt);
zm_av_packet_unref(&pkt);
} // while have buffered frames
} // end if audio_out_codec
pkt.stream_index = audio_out_stream->index;
av_interleaved_write_frame(oc, &pkt);
zm_av_packet_unref(&pkt);
} // while have buffered frames
} // end if audio_out_codec
// Flush Queues
av_interleaved_write_frame(oc, NULL);
// Flush Queues
av_interleaved_write_frame(oc, NULL);
/* Write the trailer before close */
if (int rc = av_write_trailer(oc)) {
Error("Error writing trailer %s", av_err2str(rc));
} else {
Debug(3, "Sucess Writing trailer");
/* Write the trailer before close */
if (int rc = av_write_trailer(oc)) {
Error("Error writing trailer %s", av_err2str(rc));
} else {
Debug(3, "Sucess Writing trailer");
}
// WHen will be not using a file ?
if ( !(out_format->flags & AVFMT_NOFILE) ) {
/* Close the out file. */
Debug(2, "Closing");
if (int rc = avio_close(oc->pb)) {
oc->pb = NULL;
Error("Error closing avio %s", av_err2str(rc));
}
} else {
Debug(3, "Not closing avio because we are not writing to a file.");
}
}
// I wonder if we should be closing the file first.
// I also wonder if we really need to be doing all the ctx
// allocation/de-allocation constantly, or whether we can just re-use it.
@ -463,16 +479,6 @@ VideoStore::~VideoStore() {
#endif
}
// WHen will be not using a file ?
if (!(out_format->flags & AVFMT_NOFILE)) {
/* Close the out file. */
if (int rc = avio_close(oc->pb)) {
Error("Error closing avio %s", av_err2str(rc));
}
} else {
Debug(3, "Not closing avio because we are not writing to a file.");
}
/* free the stream */
avformat_free_context(oc);
}

View File

@ -168,6 +168,8 @@ int main( int argc, char *argv[] ) {
if ( zm_reload ) {
monitor->Reload();
logTerm();
logInit( log_id_string );
zm_reload = false;
}
sigprocmask( SIG_UNBLOCK, &block_set, 0 );

View File

@ -313,6 +313,14 @@ int main(int argc, char *argv[]) {
} // end foreach n_monitors
sigprocmask(SIG_UNBLOCK, &block_set, 0);
if ( zm_reload ) {
for ( int i = 0; i < n_monitors; i++ ) {
monitors[i]->Reload();
}
logTerm();
logInit( log_id_string );
zm_reload = false;
}
} // end while ! zm_terminate
for ( int i = 0; i < n_monitors; i++ ) {
delete monitors[i];

View File

@ -1 +1 @@
1.31.17
1.31.20

View File

@ -70,6 +70,7 @@ Cache::config('default', array('engine' => 'Apc'));
*
*/
CakePlugin::load('Crud');
CakePlugin::load('CakePHP-Enum-Behavior');
/**
* You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters:
@ -142,6 +143,27 @@ foreach( $configvals as $key => $value) {
Configure::write( $key, $value );
}
// For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID
if ( ! defined('ZM_SERVER_ID') ) {
App::uses('ClassRegistry', 'Utility');
$ServerModel = ClassRegistry::init('Server');
if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) {
$Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_NAME) ) );
if ( ! $Server ) {
Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
define( 'ZM_SERVER_ID', $Server['Server']['Id'] );
}
} else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) {
$Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_HOST) ) );
if ( ! $Server ) {
Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
define( 'ZM_SERVER_ID', $Server['Server']['Id'] );
}
}
}
function process_configfile($configFile) {
if ( is_readable( $configFile ) ) {
$configvals = array();

View File

@ -13,12 +13,15 @@ class MonitorsController extends AppController {
*
* @var array
*/
public $components = array('Paginator', 'RequestHandler');
public $components = array('Paginator', 'RequestHandler');
public function beforeRender() {
$this->set($this->Monitor->enumValues());
}
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission');
if ($canView =='None') {
if ($canView == 'None') {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
@ -29,21 +32,21 @@ class MonitorsController extends AppController {
*
* @return void
*/
public function index() {
public function index() {
$this->Monitor->recursive = 0;
if ($this->request->params['named']) {
$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
} else {
$conditions = array();
}
if ($this->request->params['named']) {
$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
} else {
$conditions = array();
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
$conditions['Monitor.Id' ] = $allowedMonitors;
}
}
$find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
if ( isset( $conditions['GroupId'] ) ) {
@ -70,7 +73,7 @@ class MonitorsController extends AppController {
'monitors' => $monitors,
'_serialize' => array('monitors')
));
}
}
/**
* view method
@ -79,51 +82,57 @@ class MonitorsController extends AppController {
* @param string $id
* @return void
*/
public function view($id = null) {
$this->Monitor->recursive = 0;
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
$restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors);
} else {
$restricted = '';
}
$options = array('conditions' => array(
array('Monitor.' . $this->Monitor->primaryKey => $id),
$restricted
)
);
$monitor = $this->Monitor->find('first', $options);
$this->set(array(
'monitor' => $monitor,
'_serialize' => array('monitor')
));
}
public function view($id = null) {
$this->Monitor->recursive = 0;
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
$restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors);
} else {
$restricted = '';
}
$options = array('conditions' => array(
array('Monitor.' . $this->Monitor->primaryKey => $id),
$restricted
)
);
$monitor = $this->Monitor->find('first', $options);
$this->set(array(
'monitor' => $monitor,
'_serialize' => array('monitor')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
public function add() {
if ( $this->request->is('post') ) {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ( $this->Session->Read('systemPermission') != 'Edit' ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Monitor->create();
if ($this->Monitor->save($this->request->data)) {
$this->daemonControl($this->Monitor->id, 'start');
return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
}
}
}
$this->Monitor->create();
if ($this->Monitor->save($this->request->data)) {
$this->daemonControl($this->Monitor->id, 'start');
//return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
$message = 'Saved';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
}
/**
* edit method
@ -132,40 +141,38 @@ class MonitorsController extends AppController {
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Monitor->id = $id;
public function edit($id = null) {
$this->Monitor->id = $id;
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('monitorPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->Monitor->save($this->request->data)) {
$message = 'Saved';
} else {
$message = 'Error';
}
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('monitorPermission') != 'Edit') {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->Monitor->save($this->request->data)) {
$message = 'Saved';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
// - restart or stop this monitor after change
// - restart or stop this monitor after change
$func = $this->Monitor->find('first', array(
'fields' => array('Function'),
'conditions' => array('Id' => $id)
))['Monitor']['Function'];
// We don't pass the request data as the monitor object because it may be a subset of the full monitor array
if ( $func == 'None' ) {
$this->daemonControl( $this->Monitor->id, 'stop' );
} else {
$this->daemonControl( $this->Monitor->id, 'restart' );
$this->daemonControl( $this->Monitor->id, 'stop' );
if ( ( $func != 'None' ) and ( $this->Monitor->ServerId == ZM_SERVER_ID ) ) {
$this->daemonControl( $this->Monitor->id, 'start' );
}
}
} // end function edit
/**
* delete method
@ -174,196 +181,183 @@ class MonitorsController extends AppController {
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Monitor->id = $id;
if (!$this->Monitor->exists()) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->request->allowMethod('post', 'delete');
public function delete($id = null) {
$this->Monitor->id = $id;
if (!$this->Monitor->exists()) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('systemPermission') != 'Edit') {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->request->allowMethod('post', 'delete');
$this->daemonControl($this->Monitor->id, 'stop');
$this->daemonControl($this->Monitor->id, 'stop');
if ($this->Monitor->delete()) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
if ($this->Monitor->delete()) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
public function sourceTypes() {
$sourceTypes = $this->Monitor->query("describe Monitors Type;");
public function sourceTypes() {
$sourceTypes = $this->Monitor->query("describe Monitors Type;");
preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches);
foreach( explode(',', $matches[1]) as $value ) {
$enum[] = trim( $value, "'" );
}
preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches);
foreach( explode(',', $matches[1]) as $value ) {
$enum[] = trim( $value, "'" );
}
$this->set(array(
'sourceTypes' => $enum,
'_serialize' => array('sourceTypes')
));
}
$this->set(array(
'sourceTypes' => $enum,
'_serialize' => array('sourceTypes')
));
}
// arm/disarm alarms
// expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json
// where M=monitorId
// where C=on|off|status
public function alarm()
{
$id = $this->request->params['named']['id'];
$cmd = strtolower($this->request->params['named']['command']);
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status')
{
throw new BadRequestException(__('Invalid command'));
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
// arm/disarm alarms
// expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json
// where M=monitorId
// where C=on|off|status
public function alarm() {
$id = $this->request->params['named']['id'];
$cmd = strtolower($this->request->params['named']['command']);
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status' ) {
throw new BadRequestException(__('Invalid command'));
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
switch ($cmd)
{
case "on":
$q = '-a';
$verbose = "-v";
break;
case "off":
$q = "-c";
$verbose = "-v";
break;
case "status":
$verbose = ""; // zmu has a bug - gives incorrect verbose output in this case
$q = "-s";
break;
}
switch ($cmd) {
case 'on':
$q = '-a';
$verbose = '-v';
break;
case 'off':
$q = '-c';
$verbose = '-v';
break;
case 'status':
$verbose = ''; // zmu has a bug - gives incorrect verbose output in this case
$q = '-s';
break;
}
// form auth key based on auth credentials
$this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
// form auth key based on auth credentials
$this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value'];
$zmOptAuth = $config['Config']['Value'];
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$config = $this->Config->find('first', $options);
$zmAuthRelay = $config['Config']['Value'];
$auth="";
if ($zmOptAuth)
{
if ($zmAuthRelay == 'hashed')
{
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
$config = $this->Config->find('first', $options);
$zmAuthHashSecret = $config['Config']['Value'];
$zmAuthRelay = $config['Config']['Value'];
$auth='';
if ( $zmOptAuth ) {
if ( $zmAuthRelay == 'hashed' ) {
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
$config = $this->Config->find('first', $options);
$zmAuthHashSecret = $config['Config']['Value'];
$time = localtime();
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
$ak = md5($ak);
$auth = " -A ".$ak;
}
elseif ($zmAuthRelay == 'plain')
{
$auth = " -U " .$this->Session->Read('username')." -P ".$this->Session->Read('password');
}
elseif ($zmAuthRelay == 'none')
{
$auth = " -U " .$this->Session->Read('username');
}
}
$shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
$status = exec ($shellcmd);
$time = localtime();
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
$ak = md5($ak);
$auth = ' -A '.$ak;
} else if ( $zmAuthRelay == 'plain' ) {
$auth = ' -U ' .$this->Session->Read('username').' -P '.$this->Session->Read('password');
} else if ( $zmAuthRelay == 'none' ) {
$auth = ' -U ' .$this->Session->Read('username');
}
}
$shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
$status = exec ($shellcmd);
$this->set(array(
'status' => $status,
'_serialize' => array('status'),
));
$this->set(array(
'status' => $status,
'_serialize' => array('status'),
));
}
}
// Check if a daemon is running for the monitor id
public function daemonStatus() {
$id = $this->request->params['named']['id'];
$daemon = $this->request->params['named']['daemon'];
// Check if a daemon is running for the monitor id
public function daemonStatus() {
$id = $this->request->params['named']['id'];
$daemon = $this->request->params['named']['daemon'];
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
$monitor = $this->Monitor->find('first', array(
'fields' => array('Id', 'Type', 'Device'),
'conditions' => array('Id' => $id)
));
$monitor = $this->Monitor->find('first', array(
'fields' => array('Id', 'Type', 'Device'),
'conditions' => array('Id' => $id)
));
// Clean up the returned array
$monitor = Set::extract('/Monitor/.', $monitor);
// Clean up the returned array
$monitor = Set::extract('/Monitor/.', $monitor);
// Pass -d for local, otherwise -m
if ($monitor[0]['Type'] == 'Local') {
$args = '-d '. $monitor[0]['Device'];
} else {
$args = '-m '. $monitor[0]['Id'];
}
// Pass -d for local, otherwise -m
if ($monitor[0]['Type'] == 'Local') {
$args = "-d ". $monitor[0]['Device'];
} else {
$args = "-m ". $monitor[0]['Id'];
}
// Build the command, and execute it
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
$status = exec( $command );
// Build the command, and execute it
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
$status = exec( $command );
// If 'not' is present, the daemon is not running, so return false
// https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
// Also sending back the status text so we can check if the monitor is in pending
// state which means there may be an error
$statustext = $status;
$status = (strpos($status, 'not')) ? false : true;
// If 'not' is present, the daemon is not running, so return false
// https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
// Also sending back the status text so we can check if the monitor is in pending
// state which means there may be an error
$statustext = $status;
$status = (strpos($status, 'not')) ? false : true;
$this->set(array(
'status' => $status,
'statustext' => $statustext,
'_serialize' => array('status','statustext'),
));
}
$this->set(array(
'status' => $status,
'statustext' => $statustext,
'_serialize' => array('status','statustext'),
));
}
public function daemonControl($id, $command, $monitor=null, $daemon=null) {
$args = '';
$daemons = array();
public function daemonControl($id, $command, $monitor=null, $daemon=null) {
$args = '';
$daemons = array();
if (!$monitor) {
// Need to see if it is local or remote
$monitor = $this->Monitor->find('first', array(
'fields' => array('Type', 'Function'),
'conditions' => array('Id' => $id)
));
$monitor = $monitor['Monitor'];
}
if (!$monitor) {
// Need to see if it is local or remote
$monitor = $this->Monitor->find('first', array(
'fields' => array('Type', 'Function'),
'conditions' => array('Id' => $id)
));
$monitor = $monitor['Monitor'];
}
if ($monitor['Type'] == 'Local') {
$args = '-d ' . $monitor['Device'];
} else {
$args = '-m ' . $id;
}
if ($monitor['Type'] == 'Local') {
$args = "-d " . $monitor['Device'];
} else {
$args = "-m " . $id;
}
if ($monitor['Function'] == 'Monitor') {
array_push($daemons, 'zmc');
} else {
array_push($daemons, 'zmc', 'zma');
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
if ($monitor['Function'] == 'Monitor') {
array_push($daemons, 'zmc');
} else {
array_push($daemons, 'zmc', 'zma');
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
foreach ($daemons as $daemon) {
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
$status = exec( $shellcmd );
}
}
}
foreach ($daemons as $daemon) {
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
$status = exec( $shellcmd );
}
}
} // end class MonitorsController

View File

@ -86,10 +86,10 @@ class Monitor extends AppModel {
);
/**
* * hasMany associations
* *
* * @var array
* */
* hasMany associations
*
* @var array
*/
public $hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
@ -108,5 +108,16 @@ class Monitor extends AppModel {
'counterQuery' => ''
),
);
public $actsAs = array(
'CakePHP-Enum-Behavior.Enum' => array(
'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'),
'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'),
'Orientation' => array('0','90','180','270','hori','vert'),
'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'),
'OutputContainer' => array('auto','mp4','mkv'),
'DefaultView' => array('Events','Control'),
'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'),
)
);
}

@ -0,0 +1 @@
Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237

View File

@ -0,0 +1,2 @@
echo json_encode($message);
echo json_encode($monitor);

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $message));
echo $xml->asXML();

View File

@ -293,8 +293,8 @@ private $control_fields = array(
}
return $filters;
}
public function save( $new_values = null ) {
public function save( $new_values = null ) {
if ( $new_values ) {
foreach ( $new_values as $k=>$v ) {
@ -302,8 +302,10 @@ private $control_fields = array(
}
}
$sql = 'UPDATE Monitors SET '.implode(', ', array_map( function($field) {return $field.'=?';}, array_keys( $this->defaults ) ) ) . ' WHERE Id=?';
$values = array_map( function($field){return $this->{$field};}, $this->fields );
$fields = array_keys( $this->defaults );
$sql = 'UPDATE Monitors SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?';
$values = array_map( function($field){return $this->{$field};}, $fields );
$values[] = $this->{'Id'};
dbQuery( $sql, $values );
} // end function save

View File

@ -70,15 +70,38 @@ class Storage {
Warning( "Unknown function call Storage->$fn from $file:$line" );
}
}
public static function find_all() {
$storage_areas = array();
$result = dbQuery( 'SELECT * FROM Storage ORDER BY Name');
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage' );
foreach ( $results as $row => $obj ) {
$storage_areas[] = $obj;
$storage_cache[$obj->Id()] = $obj;
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Storage ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
return $storage_areas;
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
public function disk_usage_percent() {
$path = $this->Path();
@ -95,11 +118,8 @@ class Storage {
Error("disk_total_space returned false for " . $path );
return 0;
}
$free = disk_free_space( $path );
if ( ! $free ) {
Error("disk_free_space returned false for " . $path );
}
$usage = round(($total - $free) / $total * 100);
$used = $this->disk_used_space();
$usage = round( ($used / $total) * 100);
return $usage;
}
public function disk_total_space() {

View File

@ -1280,6 +1280,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
$filter['sql'] .= ' not regexp '.$value;
break;
case '=[]' :
case 'IN' :
$filter['sql'] .= ' in ('.join( ',', $valueList ).')';
break;
case '![]' :

View File

@ -333,7 +333,10 @@ if ($reload == 'reload') ob_start();
}
$func = function($S){ return '<span title="'.human_filesize($S->disk_used_space()) . ' of ' . human_filesize($S->disk_total_space()).'">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
#$func = function($S){ return '<span title="">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
echo implode( ', ', array_map ( $func, $storage_areas ) );
if ( count($storage_areas) >= 4 )
$storage_areas = Storage::find_all( array('ServerId'=>null) );
if ( count($storage_areas) < 4 )
echo implode( ', ', array_map ( $func, $storage_areas ) );
echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%';
?></li>
</ul>

View File

@ -175,7 +175,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
?>
<th class="colZones"><a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=zones_overview"><?php echo translate('Zones') ?></a></th>
<?php if ( canEdit('Monitors') ) { ?>
<th class="colMark"><input type="checkbox" name="toggleCheck" value="1" onclick="toggleCheckbox( this, 'markMids[]' );"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/> <?php echo translate('All') ?></th>
<th class="colMark"><input type="checkbox" name="toggleCheck" value="1" onclick="toggleCheckbox(this, 'markMids[]');setButtonStates(this);"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/> <?php echo translate('All') ?></th>
<?php } ?>
</tr>
</thead>

View File

@ -210,16 +210,16 @@ while ( $event_row = dbFetchNext( $results ) ) {
?>
<td class="colThumbnail">
<?php
if ( file_exists( $event->Path().'/snapshot.jpg' ) ) {
Warning("Using snapshot");
if ( ( $event->SaveJPEGs() == 4 ) and file_exists($event->Path().'/snapshot.jpg') ) {
Logger::Debug("Using snapshot");
$imgSrc = '?view=image&amp;eid='.$event->Id().'&amp;fid=snapshot&amp;width='.$thumbData['Width'].'&amp;height='.$thumbData['Height'];
} else {
Warning("Not Using snapshot" . $event->Path().'/snapshot.jpg' );
Logger::Debug("Not Using snapshot" . $event->Path().'/snapshot.jpg' );
$imgSrc = '?view=image&amp;eid='.$event->Id().'&amp;fid='.$thumbData['FrameId'].'&amp;width='.$thumbData['Width'].'&amp;height='.$thumbData['Height'];
}
$streamSrc = $event->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single') );
$imgHtml = '<img id="thumbnail'.$event->id().'" src="'.$imgSrc.'" alt="'. validHtmlStr('Event '.$event->Id()) .'" style="width:'. validInt($thumbData['Width']) .'px;height:'. validInt( $thumbData['Height'] ).'px;" onmouseover="this.src=\''.$streamSrc.'\';" onmouseout="this.src=\''.$imgSrc.'\';"/>';
$imgHtml = '<img id="thumbnail'.$event->id().'" src="'.$imgSrc.'" alt="'. validHtmlStr('Event '.$event->Id()) .'" style="width:'. validInt($thumbData['Width']) .'px;height:'. validInt($thumbData['Height']).'px;" onmouseover="this.src=\''.$streamSrc.'\';" onmouseout="this.src=\''.$imgSrc.'\';"/>';
echo makePopupLink(
'?view=frame&amp;eid='.$event->Id().'&amp;fid='.$thumbData['FrameId'],

View File

@ -142,6 +142,7 @@ foreach ( dbFetchAll( 'SELECT Id,Name FROM States ORDER BY lower(Name) ASC' ) as
}
$servers = array();
$servers['ZM_SERVER_ID'] = 'Current Server';
$servers['NULL'] = 'No Server';
foreach ( dbFetchAll( 'SELECT Id,Name FROM Servers ORDER BY lower(Name) ASC' ) as $server ) {
$servers[$server['Id']] = $server['Name'];
}

View File

@ -55,7 +55,17 @@ function SetImageSource( monId, time ) {
if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) {
var duration = eEndSecs[i]-eStartSecs[i];
var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1;
console.log("SetImageSource: " + time + " duration: " + duration + " frame: " + frame );
var storage = Storage[eStorageId[i]];
if ( storage.ServerId ) {
var server = Servers[storage.ServerId];
if ( server ) {
console.log( server.Hostname + " for event " + eId[i] );
return location.protocol + '//' + server.Hostname + '/index.php?view=image&eid=' + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
} else {
console.log("No server found for " + storage.ServerId );
}
}
console.log("No storage found for " + eStorageId[i] );
return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
}
} // end for
@ -465,7 +475,6 @@ function setSpeed( speed_index ) {
currentSpeed = parseFloat(speeds[speed_index]);
speedIndex = speed_index;
playSecsperInterval = Math.floor( 1000 * currentSpeed * currentDisplayInterval ) / 1000000;
console.log("playSecsPerInterval: " + playSecsperInterval + " = currentspeed:" + currentSpeed + " * " + currentDisplayInterval + " /1000");
showSpeed(speed_index);
if ( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
}

View File

@ -23,6 +23,7 @@ var imageLoadTimesNeeded=15; // and how many we need
var timeLabelsFractOfRow = 0.9;
var eMonId = [];
var eId = [];
var eStorageId = [];
var eStartSecs = [];
var eEndSecs = [];
var eventFrames = []; // this is going to presume all frames equal durationlength
@ -43,7 +44,6 @@ $index = 0;
$anyAlarms = false;
if ( ! $initialModeIsLive ) {
Warning($eventsSql);
$result = dbQuery( $eventsSql );
if ( ! $result ) {
Fatal('SQL-ERR');
@ -56,6 +56,7 @@ Warning($eventsSql);
if ( $maxTimeSecs < $event['CalcEndTimeSecs'] ) $maxTimeSecs = $event['CalcEndTimeSecs'];
echo "
eMonId[$index]=" . $event['MonitorId'] . ";
eStorageId[$index]=".$event['StorageId'] . ";
eId[$index]=" . $event['Id'] . ";
eStartSecs[$index]=" . $event['StartTimeSecs'] . ";
eEndSecs[$index]=" . $event['CalcEndTimeSecs'] . ";
@ -146,7 +147,17 @@ if ( $mId > 0 ) {
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
} // end if initialmodeislive
echo "var monitorName = [];\n";
echo "var Storage = []\n";
foreach ( Storage::find_all() as $Storage ) {
echo 'Storage[' . $Storage->Id() . '] = ' . json_encode($Storage). ";\n";
}
echo "var Servers = []\n";
foreach ( Server::find_all() as $Server ) {
echo 'Servers[' . $Server->Id() . '] = ' . json_encode($Server). ";\n";
}
echo "
var monitorName = [];\n";
echo "var monitorLoading = [];\n";
echo "var monitorImageObject = [];\n";
echo "var monitorImageURL = [];\n";

View File

@ -94,7 +94,7 @@ if (isset($_REQUEST['minTime']) && isset($_REQUEST['maxTime']) && count($display
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
$eventsSql = '
SELECT E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
SELECT E.Id,E.Name,E.StorageId,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id)
ELSE UNIX_TIMESTAMP(E.EndTime)
END AS CalcEndTimeSecs, E.Length,

View File

@ -194,7 +194,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
</div>
</form>
<?php
} else if ( $tab == "servers" ) { ?>
} else if ( $tab == 'servers' ) { ?>
<form name="serversForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
@ -210,11 +210,16 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<th class="colCpuLoad"><?php echo translate('CpuLoad') ?></th>
<th class="colMemory"><?php echo translate('Free').'/'.translate('Total') . ' ' . translate('Memory') ?></th>
<th class="colSwap"><?php echo translate('Free').'/'.translate('Total') . ' ' . translate('Swap') ?></th>
<th class="colStats"><?php echo translate('RunStats') ?></th>
<th class="colAudit"><?php echo translate('RunAudit') ?></th>
<th class="colTrigger"><?php echo translate('RunTrigger') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th>
</tr>
</thead>
<tbody>
<?php foreach( dbFetchAll( 'SELECT *,(SELECT COUNT(Id) FROM Monitors WHERE ServerId=Servers.Id) AS MonitorCount FROM Servers' ) as $row ) { ?>
<?php
foreach( dbFetchAll( 'SELECT *,(SELECT COUNT(Id) FROM Monitors WHERE ServerId=Servers.Id) AS MonitorCount FROM Servers ORDER BY Id' ) as $row ) {
?>
<tr>
<td class="colName"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Name']), $canEdit ) ?></td>
<td class="colHostname"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Hostname']), $canEdit ) ?></td>
@ -223,6 +228,9 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colCpuLoad"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server',$row['CpuLoad'], $canEdit ) ?></td>
<td class="colMemory"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeMem']) . ' / ' . human_filesize($row['TotalMem']), $canEdit ) ?></td>
<td class="colSwap"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeSwap']) . ' / ' . human_filesize($row['TotalSwap']) , $canEdit ) ?></td>
<td class="colStats"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmstats.pl'] ? 'yes' : 'no', $canEdit ) ?></td>
<td class="colAudit"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmaudit.pl'] ? 'yes' : 'no', $canEdit ) ?></td>
<td class="colTrigger"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmtrigger.pl'] ? 'yes' : 'no', $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton( this );"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr>
@ -249,6 +257,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<th class="colPath"><?php echo translate('path') ?></th>
<th class="colType"><?php echo translate('Type') ?></th>
<th class="colScheme"><?php echo translate('StorageScheme') ?></th>
<th class="colServer"><?php echo translate('Server') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th>
</tr>
</thead>
@ -260,6 +269,9 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Path']), $canEdit ) ?></td>
<td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Type']), $canEdit ) ?></td>
<td class="colScheme"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Scheme']), $canEdit ) ?></td>
<td class="colServer"><?php
$Server = new Server($row['ServerId']);
echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($Server->Name()), $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr>
<?php } #end foreach Server ?>

View File

@ -58,6 +58,27 @@ xhtmlHeaders(__FILE__, translate('Server').' - '.$newServer['Name'] );
<th scope="row"><?php echo translate('Hostname') ?></th>
<td><input type="text" name="newServer[Hostname]" value="<?php echo $newServer['Hostname'] ?>"/></td>
</tr>
<tr>
<th scope="row"><?php echo translate('RunStats') ?></th>
<td>
<input type="radio" name="newServer[zmstats.pl]" value="1"<?php echo $newServer['zmstats.pl'] ? ' checked="checked"' : '' ?>/> Yes
<input type="radio" name="newServer[zmstats.pl]" value="0"<?php echo $newServer['zmstats.pl'] ? '' : ' checked="checked"' ?>/> No
</td>
</tr>
<tr>
<th scope="row"><?php echo translate('RunAudit') ?></th>
<td>
<input type="radio" name="newServer[zmaudit.pl]" value="1"<?php echo $newServer['zmaudit.pl'] ? ' checked="checked"' : '' ?>/> Yes
<input type="radio" name="newServer[zmaudit.pl]" value="0"<?php echo $newServer['zmaudit.pl'] ? '' : ' checked="checked"' ?>/> No
</td>
</tr>
<tr>
<th scope="row"><?php echo translate('RunTrigger') ?></th>
<td>
<input type="radio" name="newServer[zmtrigger.pl]" value="1"<?php echo $newServer['zmtrigger.pl'] ? ' checked="checked"' : '' ?>/> Yes
<input type="radio" name="newServer[zmtrigger.pl]" value="0"<?php echo $newServer['zmtrigger.pl'] ? '' : ' checked="checked"' ?>/> No
</td>
</tr>
</tbody>
</table>
<div id="contentButtons">

View File

@ -27,6 +27,7 @@ if ( $_REQUEST['id'] ) {
if ( !($newStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, ARRAY($_REQUEST['id'])) ) ) {
$view = 'error';
return;
$newStorage['ServerId'] = '';
}
} else {
$newStorage = array();
@ -43,6 +44,11 @@ $scheme_options = array(
'Shallow' => translate('Shallow'),
);
$servers = Server::find_all();
$ServersById = array();
foreach ( $servers as $S ) {
$ServersById[$S->Id()] = $S;
}
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
@ -67,6 +73,10 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
<th scope="row"><?php echo translate('Path') ?></th>
<td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td>
</tr>
<tr>
<th scope="row"><?php echo translate('Server') ?></th>
<td><?php echo htmlSelect( 'newStorage[ServerId]', array('','Remote') + $ServersById, $newStorage['ServerId'] ); ?></td>
</tr>
<tr>
<th scope="row"><?php echo translate('Type') ?></th>
<td><?php echo htmlSelect( 'newStorage[Type]', $type_options, $newStorage['Type'] ); ?></td>