From 9dfad4886d569984b490ecf270e6d3033824efed Mon Sep 17 00:00:00 2001 From: Isaac Date: Fri, 22 Dec 2017 16:12:34 +0100 Subject: [PATCH 01/63] fix saving Monitors --- web/includes/Monitor.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 157088e83..b3a9ad633 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -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 From f51259443c46c0a2753014764399c9566cb49504 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 09:02:49 -0800 Subject: [PATCH 02/63] fix disk_used_percentage to use db calculated used size --- web/includes/Storage.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/web/includes/Storage.php b/web/includes/Storage.php index e14d00e92..5bbdcbbdf 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -95,11 +95,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() { From bb8e326392aecd0ecc84ef19153b993d171845fc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 09:16:56 -0800 Subject: [PATCH 03/63] Fix not restarting a monitor if it has moved to a different server --- web/api/app/Controller/MonitorsController.php | 466 +++++++++--------- 1 file changed, 226 insertions(+), 240 deletions(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index af9cc8488..c24cd12f2 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -13,7 +13,7 @@ class MonitorsController extends AppController { * * @var array */ - public $components = array('Paginator', 'RequestHandler'); + public $components = array('Paginator', 'RequestHandler'); public function beforeFilter() { parent::beforeFilter(); @@ -29,21 +29,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 +70,7 @@ class MonitorsController extends AppController { 'monitors' => $monitors, '_serialize' => array('monitors') )); - } + } /** * view method @@ -79,51 +79,51 @@ 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')); + } + } + } /** * edit method @@ -132,40 +132,39 @@ 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 +173,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 From 0d7fff761c2aa99fba4645d2a06f9353ce9be3de Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 11:38:42 -0800 Subject: [PATCH 04/63] upgrade find_all to take parameters --- web/includes/Storage.php | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 5bbdcbbdf..44749089a 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -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(); From 0c350b8f4157f32b5055b50e86f42a7c574144a8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 11:38:59 -0800 Subject: [PATCH 05/63] add ServerId to Storage --- db/zm_create.sql.in | 1 + db/zm_update-1.31.18.sql | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 db/zm_update-1.31.18.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index b5e58d422..ef13ab924 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -640,6 +640,7 @@ CREATE TABLE `Storage` ( `Type` enum('local','s3fs') NOT NULL default 'local', `DiskSpace` bigint unsigned default NULL, `Scheme enum('Deep','Medium','Shallow') NOT NULL default 'Medium', + `ServerId` int(10) unsigned, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; diff --git a/db/zm_update-1.31.18.sql b/db/zm_update-1.31.18.sql new file mode 100644 index 000000000..56e15dd3a --- /dev/null +++ b/db/zm_update-1.31.18.sql @@ -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 = '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; From c6bf502ee73f1c20e710dd935803657a2d78127f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 11:39:27 -0800 Subject: [PATCH 06/63] only show each Storage area if there are less than 4. --- web/skins/classic/includes/functions.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 4a2304965..e6a0e1fe4 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -320,7 +320,10 @@ if ($reload == 'reload') ob_start(); } $func = function($S){ return ''.$S->Name() . ': ' . $S->disk_usage_percent().'%' . ''; }; #$func = function($S){ return ''.$S->Name() . ': ' . $S->disk_usage_percent().'%' . ''; }; - 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).'%'; ?> From 2c361de9d3547534023fa702e73185e1b0d2dec1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 11:39:43 -0800 Subject: [PATCH 07/63] add Server Storage Areas --- web/skins/classic/views/options.php | 6 +++++- web/skins/classic/views/storage.php | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 443fe4337..2ca14f71d 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -194,7 +194,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI +} else if ( $tab == 'servers' ) { ?>
@@ -249,6 +249,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI + @@ -260,6 +261,9 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI + Name()), $canEdit ) ?> disabled="disabled"/> diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index d74ea9079..bca0cdb04 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -43,6 +43,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 +72,10 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] ); + + + + From 74269fea73a53d5a558d1f7fd5c6699144988db4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 22 Dec 2017 12:32:45 -0800 Subject: [PATCH 08/63] make montagereview load event images from the server that the storage is located on --- web/includes/functions.php | 1 + web/skins/classic/views/js/montagereview.js | 13 +++++++++++-- web/skins/classic/views/js/montagereview.js.php | 15 +++++++++++++-- web/skins/classic/views/montagereview.php | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 10ee34015..fed583f03 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1280,6 +1280,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) { $filter['sql'] .= ' not regexp '.$value; break; case '=[]' : + case 'IN' : $filter['sql'] .= ' in ('.join( ',', $valueList ).')'; break; case '![]' : diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 0c0237f85..e1f854f66 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -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 } diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index b1988ed86..19089a315 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -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"; diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 3452b2f5f..56dadabd1 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -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, From cc3b65b5fd924f741b3f5df2e5869e3fb7a751fa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Dec 2017 12:07:13 -0800 Subject: [PATCH 09/63] add default value for ServerId in Storage --- web/skins/classic/views/storage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index bca0cdb04..c5962ba01 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -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(); From b219f76588ed8431ee087d78d25fc47bb236cbd0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Dec 2017 12:58:48 -0800 Subject: [PATCH 10/63] add togglebuttons to select all checbox --- web/skins/classic/views/console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index f85cd5afa..c8ec29020 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -175,7 +175,7 @@ xhtmlHeaders( __FILE__, translate('Console') ); ?> - disabled="disabled"/> + disabled="disabled"/> From 7fcbd86ba0bd4c7c3b3e4efd27529dc3df274b48 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Dec 2017 15:29:57 -0500 Subject: [PATCH 11/63] fix auditing Medium directory structures --- scripts/zmaudit.pl.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 2de067c3e..e1c97e448 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -238,8 +238,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() ); From a79017b16888b30d819a14936f02c379b2c06cfa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 12:43:47 -0500 Subject: [PATCH 12/63] add a KEY to Events for EndTime and DiskSpace, since we are constantly selecting on those. Insert the Update DiskSpace filter on db creation. --- db/zm_create.sql.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index ef13ab924..b2d62d461 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -209,11 +209,12 @@ 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`), + PRIMARY KEY (`Id`), KEY `MonitorId` (`MonitorId`), KEY `StartTime` (`StartTime`), KEY `Frames` (`Frames`), - KEY `Archived` (`Archived`) + KEY `Archived` (`Archived`), + KEY `EndTime_DiskSpace` (`EndTime`,`DiskSpace`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- @@ -670,6 +671,7 @@ 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); +insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}],"sort_field":"Id","sort_asc":"1","limit":"100"}',0,0,0,0,0,0,'',1,1,1,0); -- -- Add in some sample control protocol definitions From edd5bd8874f04b921f6a3847a48a4b5345bda305 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 12:44:09 -0500 Subject: [PATCH 13/63] create the Update DIskSpace filter if it doesn't exist --- db/zm_update-1.31.18.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/db/zm_update-1.31.18.sql b/db/zm_update-1.31.18.sql index 56e15dd3a..fd000bcfa 100644 --- a/db/zm_update-1.31.18.sql +++ b/db/zm_update-1.31.18.sql @@ -10,3 +10,14 @@ SET @s = (SELECT IF( 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"}],"sort_field":"Id","sort_asc":"1","limit":"100"}' + ) > 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\"}],\"sort_field\":\"Id\",\"sort_asc\":\"1\",\"limit\":\"100\"}',1,1)" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; From 1cd69c69b98b67ecb93c8d95f712589c8d3a7c1c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 13:10:39 -0500 Subject: [PATCH 14/63] add defining ZM_SERVER_ID after loading config --- web/api/app/Config/bootstrap.php.in | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/web/api/app/Config/bootstrap.php.in b/web/api/app/Config/bootstrap.php.in index cc096f233..d1cd7857d 100644 --- a/web/api/app/Config/bootstrap.php.in +++ b/web/api/app/Config/bootstrap.php.in @@ -142,6 +142,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(); From 6c07347721065789470c570e151b44a6818a499f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 13:19:16 -0500 Subject: [PATCH 15/63] Fix Update DiskSpace Filter adding --- db/zm_create.sql.in | 21 +++++++++++++++++++-- db/zm_update-1.31.18.sql | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index b2d62d461..f9ac3b6d2 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -670,8 +670,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); -insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}],"sort_field":"Id","sort_asc":"1","limit":"100"}',0,0,0,0,0,0,'',1,1,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 diff --git a/db/zm_update-1.31.18.sql b/db/zm_update-1.31.18.sql index fd000bcfa..5ba3b6caa 100644 --- a/db/zm_update-1.31.18.sql +++ b/db/zm_update-1.31.18.sql @@ -13,10 +13,10 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM Filters WHERE Name = 'Update DiskSpace' - AND Query = '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}],"sort_field":"Id","sort_asc":"1","limit":"100"}' + 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\"}],\"sort_field\":\"Id\",\"sort_asc\":\"1\",\"limit\":\"100\"}',1,1)" + "INSERT INTO Filters (Name,Query,UpdateDiskSpace,Background) values ('Update DiskSpace','{\"terms\":[{\"attr\":\"DiskSpace\",\"op\":\"IS\",\"val\":\"NULL\"}]}',1,1)" )); PREPARE stmt FROM @s; From b62343fb2c8adebc6d1f8a6a0182e023a8ddcbde Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 13:19:30 -0500 Subject: [PATCH 16/63] bump to 1.31.18 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 874058d96..d1be1a9fa 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.17 +1.31.18 From 1b1b93f8111e50dfcc5ce9f614009a299cfccf68 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 1 Jan 2018 14:43:02 -0500 Subject: [PATCH 17/63] use CakePHP-Enum-Behavior to add support for ENUMs to the Monitor model. This should fix #48 --- .gitmodules | 3 +++ web/api/app/Config/bootstrap.php.in | 1 + web/api/app/Controller/MonitorsController.php | 22 +++++++++++++------ web/api/app/Model/Monitor.php | 19 ++++++++++++---- web/api/app/Plugin/CakePHP-Enum-Behavior | 1 + web/api/app/View/Monitors/json/add.ctp | 2 ++ web/api/app/View/Monitors/xml/add.ctp | 2 ++ 7 files changed, 39 insertions(+), 11 deletions(-) create mode 160000 web/api/app/Plugin/CakePHP-Enum-Behavior create mode 100644 web/api/app/View/Monitors/json/add.ctp create mode 100644 web/api/app/View/Monitors/xml/add.ctp diff --git a/.gitmodules b/.gitmodules index 0fa8df21a..fc8ea09d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/web/api/app/Config/bootstrap.php.in b/web/api/app/Config/bootstrap.php.in index d1cd7857d..4e2d99c63 100644 --- a/web/api/app/Config/bootstrap.php.in +++ b/web/api/app/Config/bootstrap.php.in @@ -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: diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index c24cd12f2..0880cc68e 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -15,10 +15,13 @@ class MonitorsController extends AppController { */ 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; } @@ -109,10 +112,9 @@ class MonitorsController extends AppController { * @return void */ public function add() { - if ($this->request->is('post')) { + if ( $this->request->is('post') ) { - if ($this->Session->Read('systemPermission') != 'Edit') - { + if ( $this->Session->Read('systemPermission') != 'Edit' ) { throw new UnauthorizedException(__('Insufficient privileges')); return; } @@ -120,8 +122,15 @@ class MonitorsController extends AppController { $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')); + //return $this->flash(__('The monitor has been saved.'), array('action' => 'index')); + $message = 'Saved'; + } else { + $message = 'Error'; } + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); } } @@ -138,8 +147,7 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - if ($this->Session->Read('monitorPermission') != 'Edit') - { + if ($this->Session->Read('monitorPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); return; } diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 0504dd22a..c9697b694 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -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'), + ) + ); } diff --git a/web/api/app/Plugin/CakePHP-Enum-Behavior b/web/api/app/Plugin/CakePHP-Enum-Behavior new file mode 160000 index 000000000..7108489f2 --- /dev/null +++ b/web/api/app/Plugin/CakePHP-Enum-Behavior @@ -0,0 +1 @@ +Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237 diff --git a/web/api/app/View/Monitors/json/add.ctp b/web/api/app/View/Monitors/json/add.ctp new file mode 100644 index 000000000..77d2dd08b --- /dev/null +++ b/web/api/app/View/Monitors/json/add.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($monitor); diff --git a/web/api/app/View/Monitors/xml/add.ctp b/web/api/app/View/Monitors/xml/add.ctp new file mode 100644 index 000000000..09fb8979a --- /dev/null +++ b/web/api/app/View/Monitors/xml/add.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $message)); +echo $xml->asXML(); From 16f01d5ecbdf1b8b0a96fd05d343cac8ec70e190 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jan 2018 07:10:38 -0800 Subject: [PATCH 18/63] Fix when specifying by filter_id or filter_name. Probably a botched merge. --- scripts/zmfilter.pl.in | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 625693bd3..4db95e58b 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -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 { From 867f2a916677df6dc8b1a054197dd88d384fcdf6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jan 2018 09:15:50 -0800 Subject: [PATCH 19/63] simplify deleting event files. The old way was too intensive. --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 87 +++++++++++----------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 1194ea56c..4bf71fa82 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -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 From 642c3f053a2da5b7db0b06352916d0a1fba63cf0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jan 2018 09:16:37 -0800 Subject: [PATCH 20/63] We are not using boost at this time. --- CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c001b98c..8b8308afc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 From 485741b6f7acd0f9d3265d878122dfa5c7231078 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jan 2018 12:44:17 -0800 Subject: [PATCH 21/63] treat File exists as a non-error --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 4bf71fa82..52a25ad17 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -457,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"; } } } From 4e24e3647e255ee940c674f3b4bf04439f7e6d2d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Jan 2018 07:20:20 -0800 Subject: [PATCH 22/63] Also join the Storage table, and use it for ServerId instead of Monitors table. --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 346aaa985..1491f05ef 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -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 )"; From 2ec25a20ec68659a777235da14861be4ed5e8655 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 10:07:51 -0500 Subject: [PATCH 23/63] Don't crash if we fail to open the video output stream --- src/zm_videostore.cpp | 118 ++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 2fa8617c1..1b8ad0028 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -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); } From 10133073de918f4e3dfeced84bf4ea9bf7ddd386 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 12:31:20 -0500 Subject: [PATCH 24/63] Check for SaveJPEGs being set to snapshot instead of hitting the fs to check for a snapshot file --- web/skins/classic/views/events.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 7af36112d..4be96d4ed 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -210,16 +210,16 @@ while ( $event_row = dbFetchNext( $results ) ) { ?> Path().'/snapshot.jpg' ) ) { -Warning("Using snapshot"); + if ( ( $event->SaveJPEGs() == 4 ) and file_exists($event->Path().'/snapshot.jpg') ) { + Logger::Debug("Using snapshot"); $imgSrc = '?view=image&eid='.$event->Id().'&fid=snapshot&width='.$thumbData['Width'].'&height='.$thumbData['Height']; } else { -Warning("Not Using snapshot" . $event->Path().'/snapshot.jpg' ); + Logger::Debug("Not Using snapshot" . $event->Path().'/snapshot.jpg' ); $imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&height='.$thumbData['Height']; } $streamSrc = $event->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single') ); - $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; + $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; echo makePopupLink( '?view=frame&eid='.$event->Id().'&fid='.$thumbData['FrameId'], From 2cc185fd457507c5a23b227cb6d6669411d351e8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 13:06:28 -0500 Subject: [PATCH 25/63] Move Updating Storage stats to a trigger. Remove the code from zmwatch that did it before --- distros/ubuntu1204/zoneminder.postinst | 4 ++-- distros/ubuntu1604/zoneminder.postinst | 4 ++-- scripts/zmwatch.pl.in | 10 ---------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst index 10bdaca2e..7d9a9fe8a 100644 --- a/distros/ubuntu1204/zoneminder.postinst +++ b/distros/ubuntu1204/zoneminder.postinst @@ -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 diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 659746bb4..35d839cc2 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -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 diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 0ecbb7278..afd9acc11 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -201,16 +201,6 @@ while( 1 ) { } # 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) From 4033cf5ffc06fa36d8e72c66b4534aeb6abe6a50 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 13:07:02 -0500 Subject: [PATCH 26/63] bump version to 1.31.19 with triggers --- db/zm_update-1.31.19.sql | 96 ++++++++++++++++++++++++++++++++++++++++ version | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 db/zm_update-1.31.19.sql diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql new file mode 100644 index 000000000..bd46cc968 --- /dev/null +++ b/db/zm_update-1.31.19.sql @@ -0,0 +1,96 @@ +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 + + call update_storage_stats(OLD.StorageId, diff); + ELSE + call update_storage_stats(NEW.StorageId, NEW.DiskSpace); + call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); + 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 ; + diff --git a/version b/version index d1be1a9fa..eb3e9fedd 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.18 +1.31.19 From db0e35bf55b8bcc8d98517c280e5cec4b4c04706 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 15:04:13 -0500 Subject: [PATCH 27/63] Move eventcounts update from zmwatch.pl to triggers and zmaudit.pl --- db/zm_update-1.31.19.sql | 38 ++++++++++++++++++++++++++++++++++++++ scripts/zmaudit.pl.in | 20 ++++++++++++++++++++ scripts/zmwatch.pl.in | 21 --------------------- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql index bd46cc968..1c1b02b38 100644 --- a/db/zm_update-1.31.19.sql +++ b/db/zm_update-1.31.19.sql @@ -18,6 +18,40 @@ end; delimiter ; +drop procedure if exists update_monitor_stats; + +delimiter // + +create procedure update_monitor_stats(IN MonitorId int(10), IN increment INT, IN space BIGINT) + +sql security invoker + +deterministic + +begin + + update Monitors set + TotalEvents = TotalEvents + increment, + TotalEventDiskSpace = TotalEventDiskSpace + increment * space, + HourEvents = HourEvents + increment, + HourEventDiskSpace = HourEventDiskSpace + increment * space, + DayEvents = DayEvents + increment, + DayEventDiskSpace = DayEventDiskSpace + increment * space, + WeekEvents = WeekEvents + increment, + WeekEventDiskSpace = WeekEventDiskSpace + increment * space, + MonthEvents = MonthEvents + increment, + MonthEventDiskSpace = MonthEventDiskSpace + increment * space, + ArchivedEvents = ArchivedEvents + increment, + ArchivedEventDiskSpace = ArchivedEventDiskSpace + increment * space + where Id = MonitorId; + +end; + +// + +delimiter ; + + drop trigger if exists event_update_trigger; delimiter // @@ -42,6 +76,8 @@ begin call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); END IF; + call update_monitor_stats( OLD.MonitorId, -1, OLD.DiskSpace ); + call update_monitor_stats( NEW.MonitorId, 1, NEW.DiskSpace ); end; @@ -64,6 +100,7 @@ for each row begin call update_storage_stats(NEW.StorageId, NEW.DiskSpace); + call update_monitor_stats( NEW.MonitorId, 1, NEW.DiskSpace ); end; @@ -87,6 +124,7 @@ for each row begin call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); + call update_monitor_stats( OLD.MonitorId, -1, OLD.DiskSpace ); end; diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index e1c97e448..a05d631be 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -593,6 +593,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; }; diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index afd9acc11..e2f8197a3 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -79,31 +79,11 @@ 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; @@ -199,7 +179,6 @@ while( 1 ) { } # end foreach monitor - $eventcounts_sth->finish(); sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); } # end while (1) From 68166719ca440700db4ac22c6a4e8090f4fb5581 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 17:15:46 -0500 Subject: [PATCH 28/63] Update the SQL that updates the monitor counts to be more efficient. --- scripts/zmwatch.pl.in | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index e2f8197a3..2fc1b26be 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -79,11 +79,47 @@ 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 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 + 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; @@ -179,6 +215,7 @@ while( 1 ) { } # end foreach monitor + $eventcounts_sth->finish(); sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); } # end while (1) From 224e261ade2694ef692b6f6fb89f811abe17c311 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 17:16:15 -0500 Subject: [PATCH 29/63] Don't run audit on default storage area. Add a test for an event having no endtime --- scripts/zmaudit.pl.in | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index a05d631be..39f5cdaee 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -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() ) ) { @@ -362,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' ); From 796ba0d899cc0192df8f13ff2a69b4fbc5a2bd7b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 17:16:43 -0500 Subject: [PATCH 30/63] revert to using the mysql client to do updates. We need it for delemiter changes when adding triggers --- scripts/zmupdate.pl.in | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index bcb12d3ce..5e7b72411 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -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 { From b7ad24a26662eba173730a7c3818ab0c0fb26874 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jan 2018 17:22:51 -0500 Subject: [PATCH 31/63] revert the triggers to update Monitor Stats. THey just don't work --- db/zm_update-1.31.19.sql | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql index 1c1b02b38..a33f94944 100644 --- a/db/zm_update-1.31.19.sql +++ b/db/zm_update-1.31.19.sql @@ -18,40 +18,6 @@ end; delimiter ; -drop procedure if exists update_monitor_stats; - -delimiter // - -create procedure update_monitor_stats(IN MonitorId int(10), IN increment INT, IN space BIGINT) - -sql security invoker - -deterministic - -begin - - update Monitors set - TotalEvents = TotalEvents + increment, - TotalEventDiskSpace = TotalEventDiskSpace + increment * space, - HourEvents = HourEvents + increment, - HourEventDiskSpace = HourEventDiskSpace + increment * space, - DayEvents = DayEvents + increment, - DayEventDiskSpace = DayEventDiskSpace + increment * space, - WeekEvents = WeekEvents + increment, - WeekEventDiskSpace = WeekEventDiskSpace + increment * space, - MonthEvents = MonthEvents + increment, - MonthEventDiskSpace = MonthEventDiskSpace + increment * space, - ArchivedEvents = ArchivedEvents + increment, - ArchivedEventDiskSpace = ArchivedEventDiskSpace + increment * space - where Id = MonitorId; - -end; - -// - -delimiter ; - - drop trigger if exists event_update_trigger; delimiter // @@ -76,9 +42,6 @@ begin call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); END IF; - call update_monitor_stats( OLD.MonitorId, -1, OLD.DiskSpace ); - call update_monitor_stats( NEW.MonitorId, 1, NEW.DiskSpace ); - end; // @@ -100,7 +63,6 @@ for each row begin call update_storage_stats(NEW.StorageId, NEW.DiskSpace); - call update_monitor_stats( NEW.MonitorId, 1, NEW.DiskSpace ); end; @@ -124,7 +86,6 @@ for each row begin call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); - call update_monitor_stats( OLD.MonitorId, -1, OLD.DiskSpace ); end; From 37a40a30d30665d15d4a9c34b8b115d6d04dec59 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jan 2018 14:39:54 -0500 Subject: [PATCH 32/63] drop archived and Frames indexes from Events, as they are useless. Add a StorageId Index --- db/zm_update-1.31.19.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql index a33f94944..1ca3a7893 100644 --- a/db/zm_update-1.31.19.sql +++ b/db/zm_update-1.31.19.sql @@ -93,3 +93,7 @@ end; delimiter ; +ALTER TABLE Events DROP INDEX Archived; +ALTER TABLE Events DROP INDEX Frames; +CREATE INDEX Events_StorageId_idx on Events (StorageId); + From 5c75df1a40789d73a0e4e9d077090dcf2468428b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jan 2018 14:40:03 -0500 Subject: [PATCH 33/63] drop archived and Frames indexes from Events, as they are useless. Add a StorageId Index --- db/zm_create.sql.in | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index f9ac3b6d2..917d3d4c1 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -210,11 +210,10 @@ CREATE TABLE `Events` ( `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `DiskSpace` bigint unsigned default NULL, PRIMARY KEY (`Id`), - KEY `MonitorId` (`MonitorId`), - KEY `StartTime` (`StartTime`), - KEY `Frames` (`Frames`), - KEY `Archived` (`Archived`), - KEY `EndTime_DiskSpace` (`EndTime`,`DiskSpace`) + 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@; -- From b25452d4c54ad3b3df46f2d2d4aee7a3ba89f6fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jan 2018 15:47:28 -0500 Subject: [PATCH 34/63] Don't both to add the Floreon control type. That can be done with zmcamtool --- db/zm_update-1.31.2.sql | 3 --- 1 file changed, 3 deletions(-) diff --git a/db/zm_update-1.31.2.sql b/db/zm_update-1.31.2.sql index 9761987d1..d918db17b 100644 --- a/db/zm_update-1.31.2.sql +++ b/db/zm_update-1.31.2.sql @@ -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); - From 727bde752f26407be598ad459d494ccae0b8a9f6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 7 Jan 2018 13:39:13 -0500 Subject: [PATCH 35/63] Count non-i frames in frames_to_keep. Should reduce the size of the packetqueue --- src/zm_packetqueue.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index f55a7a680..4972310e8 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -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 ); } From baa360cd3df30d79694ce8376277b4bd12ee2aa3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 7 Jan 2018 14:20:17 -0500 Subject: [PATCH 36/63] update event stat counts for all monitors at once --- scripts/zmwatch.pl.in | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 2fc1b26be..54964d3b0 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -108,17 +108,17 @@ UPDATE Monitors INNER JOIN ( Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, Monitors.MonthEvents = E.MonthEvents, Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace - WHERE Id=? `; my $eventcounts_sth = $dbh->prepare_cached( $eventcounts_sql ); while( 1 ) { + $eventcounts_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); + 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'; @@ -212,14 +212,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(); sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); } # end while (1) +$eventcounts_sth->finish(); + Info( "Watchdog exiting\n" ); exit(); 1; From e877fa1d61b748876a71f658738252b7c76dffce Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 8 Jan 2018 14:49:57 -0500 Subject: [PATCH 37/63] make zmupdate_1.31.19 re-runable. --- db/zm_update-1.31.19.sql | 24 +++++++++++++++++++++--- scripts/zmwatch.pl.in | 1 - 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql index 1ca3a7893..43c9c5005 100644 --- a/db/zm_update-1.31.19.sql +++ b/db/zm_update-1.31.19.sql @@ -93,7 +93,25 @@ end; delimiter ; -ALTER TABLE Events DROP INDEX Archived; -ALTER TABLE Events DROP INDEX Frames; -CREATE INDEX Events_StorageId_idx on Events (StorageId); +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); diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 54964d3b0..2c55a8b75 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -119,7 +119,6 @@ while( 1 ) { or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { - my $now = time(); next if $monitor->{Function} eq 'None'; my $restart = 0; From 348764985bc724e5806a33aceb590b9d719f6bc6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 8 Jan 2018 15:42:28 -0500 Subject: [PATCH 38/63] Fix Storage DiskSpace updating when Event.DiskSpace IS NULL --- db/zm_update-1.31.19.sql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/db/zm_update-1.31.19.sql b/db/zm_update-1.31.19.sql index 43c9c5005..f23addd69 100644 --- a/db/zm_update-1.31.19.sql +++ b/db/zm_update-1.31.19.sql @@ -36,10 +36,16 @@ begin 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; @@ -51,7 +57,7 @@ delimiter ; drop trigger if exists event_insert_trigger; delimiter // - +/* create trigger event_insert_trigger after insert @@ -65,7 +71,7 @@ begin call update_storage_stats(NEW.StorageId, NEW.DiskSpace); end; - +*/ // delimiter ; From 3e31203a22467278fc6f092edba5596533821ba5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 8 Jan 2018 16:11:54 -0500 Subject: [PATCH 39/63] default service start timeout to 5 minutes. THe default of 1.5 minutes is too short on some larger systems --- distros/ubuntu1604/zoneminder.service | 1 + 1 file changed, 1 insertion(+) diff --git a/distros/ubuntu1604/zoneminder.service b/distros/ubuntu1604/zoneminder.service index 04bbc5875..ac719b733 100644 --- a/distros/ubuntu1604/zoneminder.service +++ b/distros/ubuntu1604/zoneminder.service @@ -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 From 2427a5bf6376bdc73c9a611fcc476f7e03b19736 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 8 Jan 2018 16:13:11 -0500 Subject: [PATCH 40/63] sync up .service file to match 1604. --- distros/ubuntu1204/zoneminder.service | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/distros/ubuntu1204/zoneminder.service b/distros/ubuntu1204/zoneminder.service index d5b326d59..ac719b733 100644 --- a/distros/ubuntu1204/zoneminder.service +++ b/distros/ubuntu1204/zoneminder.service @@ -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 From b2fbd976cb8713728f1f2a324174f788ed57bded Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 8 Jan 2018 16:21:51 -0500 Subject: [PATCH 41/63] double the amount of time we wait for a process to end --- scripts/zmdc.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 5d422bb3f..1e6ddc2fe 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -234,6 +234,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; @@ -505,7 +506,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 +527,6 @@ sub _stop { my $pid = send_stop( $final, $process ); return if ! $pid; delete( $cmd_hash{$$process{command}} ); - kill_until_dead( $process ); } From 4047d0ffc8506a604b725775be0da52ca386a798 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jan 2018 07:13:03 -0800 Subject: [PATCH 42/63] add No Server as an option is Server filter --- web/skins/classic/views/filter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index 5bbe020c7..d01204d91 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -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']; } From e28b6c8b7c0c5d17d2e46a166eb001f40134562d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jan 2018 07:28:57 -0800 Subject: [PATCH 43/63] When server is NULL, don't escape the NULL --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 1491f05ef..ed5779b2b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -213,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 From 1db4d7df8325e2eed9c14137fd7082be622316f3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jan 2018 16:35:28 -0500 Subject: [PATCH 44/63] start work on Events_(Hour|Day|Month|etc) tables for the event count data --- db/zm_create.sql.in | 41 ++++++++++ db/zm_update-1.31.20.sql | 160 +++++++++++++++++++++++++++++++++++++++ scripts/zmwatch.pl.in | 52 ++++++++++++- 3 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 db/zm_update-1.31.20.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 917d3d4c1..d2369369b 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -216,6 +216,47 @@ CREATE TABLE `Events` ( 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@; + -- -- Table structure for table `Filters` -- diff --git a/db/zm_update-1.31.20.sql b/db/zm_update-1.31.20.sql new file mode 100644 index 000000000..1cbc287b2 --- /dev/null +++ b/db/zm_update-1.31.20.sql @@ -0,0 +1,160 @@ + +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; + +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 + + 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; +end; + +// + +delimiter ; + diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 2c55a8b75..5624e2372 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -110,10 +110,60 @@ UPDATE Monitors INNER JOIN ( 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 ) { - $eventcounts_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); + $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() ); my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) or Fatal( "Can't execute: ".$sth->errstr() ); From acd12d234a9db634d30a1432c18fad2f91331c28 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:26:59 -0500 Subject: [PATCH 45/63] add Events_Archived table. Add zmstats, zmaudit and zmtrigger settings to Servers table to tell if we should run the services or not. Fix a missing backtick at Scheme --- db/zm_create.sql.in | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index d2369369b..72f8f7681 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -257,6 +257,16 @@ CREATE TABLE `Events_Month` ( 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@; + -- -- Table structure for table `Filters` -- @@ -542,6 +552,9 @@ CREATE TABLE `Servers` ( `FreeMem` bigint unsigned default null, `TotalSwap` bigint unsigned default null, `FreeSwap` bigint unsigned default null, + `zmstats` BOOLEAN NOT NULL DEFAULT FALSE, + `zmaudit` BOOLEAN NOT NULL DEFAULT FALSE, + `zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -680,7 +693,7 @@ 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@; From c6ae965b9c3111a56db5a1d0fa36540e39f66beb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:27:42 -0500 Subject: [PATCH 46/63] add Events_Archived and update triggers to update Archived and Total Events --- db/zm_update-1.31.20.sql | 126 ++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 20 deletions(-) diff --git a/db/zm_update-1.31.20.sql b/db/zm_update-1.31.20.sql index 1cbc287b2..e1317caab 100644 --- a/db/zm_update-1.31.20.sql +++ b/db/zm_update-1.31.20.sql @@ -85,37 +85,81 @@ CREATE TABLE `Events_Month` ( 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 +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); + call update_storage_stats(OLD.StorageId, diff); END IF; ELSE IF ( NEW.DiskSpace ) THEN - call update_storage_stats(NEW.StorageId, NEW.DiskSpace); + call update_storage_stats(NEW.StorageId, NEW.DiskSpace); END IF; IF ( OLD.DiskSpace ) THEN - call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); + call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); END IF; END IF; -end; + 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 Events_Archived_Count = Events_Archived_Count+1, Events_Archived_DiskSpace = Events_Archived_DiskSpace + NEW.DiskSpace WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET Events_Archived_Count = Events_Archived_Count-1, Events_Archived_DiskSpace = Events_Archived_DiskSpace - 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 + Events_Archived_DiskSpace = Events_Archived_DiskSpace - OLD.DiskSpace + NEW.DiskSpace + WHERE Id=OLD.MonitorId; + END IF; + END IF; + END IF; + + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Monitors SET Events_Total_DiskSpace = Events_Total_DiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId; + END IF; + +END; // delimiter ; -drop trigger if exists event_insert_trigger; +DROP TRIGGER IF EXISTS event_insert_trigger; delimiter // create trigger event_insert_trigger after insert on Events @@ -126,7 +170,6 @@ for each row 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; // @@ -137,24 +180,67 @@ drop trigger if exists event_delete_trigger; delimiter // -create trigger event_delete_trigger - -before delete - -on Events - -for each row - -begin +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; -end; + IF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET + Events_Archived = Events_Archived - 1, + Events_Archived_DiskSpace = Events_Archived_DiskSpace - OLD.DiskSpace, + Events_Total_Count = Events_Total_Count - 1, + Events_Total_DiskSpace = Events_Total_DiskSpace - OLD.DiskSpace + WHERE Id=OLD.MonitorId; + ELSE + UPDATE Monitors SET + Events_Total_Count = Events_Total_Count-1, + Events_Total_DiskSpace=Events_Total_DiskSpace-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 = 'Storage' + AND column_name = 'zmstats.pl' + ) > 0, + "SELECT 'Column zmstats.pl already exists in Storage'", + "ALTER TABLE Storage ADD `zmstats.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `DiskSpace`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Storage' + AND column_name = 'zmaudit.pl' + ) > 0, + "SELECT 'Column zmaudit.pl already exists in Storage'", + "ALTER TABLE Storage 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 = 'Storage' + AND column_name = 'zmtrigger.pl' + ) > 0, + "SELECT 'Column zmtriger.pl already exists in Storage'", + "ALTER TABLE Storage ADD `zmtrigger.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmaudit.pl`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; From 92bc6d6c204ba089bb4d8640692ee49345c6f32a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:55:42 -0500 Subject: [PATCH 47/63] put back the .pl --- db/zm_create.sql.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 72f8f7681..9a8665933 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -552,9 +552,9 @@ CREATE TABLE `Servers` ( `FreeMem` bigint unsigned default null, `TotalSwap` bigint unsigned default null, `FreeSwap` bigint unsigned default null, - `zmstats` BOOLEAN NOT NULL DEFAULT FALSE, - `zmaudit` BOOLEAN NOT NULL DEFAULT FALSE, - `zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE, + `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@; From 4908ca17328ae0694acb4ab7175ba0a36ed7b8c3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:56:01 -0500 Subject: [PATCH 48/63] add the zmstats.pl, etc to Servers instead of Stroage --- db/zm_update-1.31.20.sql | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/db/zm_update-1.31.20.sql b/db/zm_update-1.31.20.sql index e1317caab..17d36bea6 100644 --- a/db/zm_update-1.31.20.sql +++ b/db/zm_update-1.31.20.sql @@ -211,11 +211,11 @@ delimiter ; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() - AND table_name = 'Storage' + AND table_name = 'Servers' AND column_name = 'zmstats.pl' ) > 0, - "SELECT 'Column zmstats.pl already exists in Storage'", - "ALTER TABLE Storage ADD `zmstats.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `DiskSpace`" + "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; @@ -223,11 +223,11 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() - AND table_name = 'Storage' + AND table_name = 'Servers' AND column_name = 'zmaudit.pl' ) > 0, - "SELECT 'Column zmaudit.pl already exists in Storage'", - "ALTER TABLE Storage ADD `zmaudit.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmstats.pl`" + "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; @@ -235,11 +235,11 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() - AND table_name = 'Storage' + AND table_name = 'Servers' AND column_name = 'zmtrigger.pl' ) > 0, - "SELECT 'Column zmtriger.pl already exists in Storage'", - "ALTER TABLE Storage ADD `zmtrigger.pl` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmaudit.pl`" + "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; From 146d56a6a0f656777303b5c6be8016ea914d138d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:56:36 -0500 Subject: [PATCH 49/63] fix missing } --- scripts/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index f69a71ab1..81cfdaa56 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -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) From cdbe1b9216c791a26ecf43a477d10c8e5dcc4c3b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:57:37 -0500 Subject: [PATCH 50/63] Only load SYs::MemInfo and Sys::CpuLoad if we are going to use them. Only start various services if they are turned on --- scripts/zmdc.pl.in | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 1e6ddc2fe..ec8e8635f 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -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()); @@ -282,6 +284,13 @@ sub run { my $win = $rin; my $ein = $win; my $timeout = 1; + my $Server = undef; + + if ( $Config{ZM_SERVER_ID} ) { + require ZoneMinder::Server; + $Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} ); + } + while( 1 ) { if ( $Config{ZM_SERVER_ID} ) { $dbh = zmDbConnect() if ! $dbh->ping(); @@ -297,12 +306,16 @@ sub run { my $paddr = accept( CLIENT, SERVER ); my $message = ; - 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' ) { @@ -578,7 +591,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} ); } } From 4f6ab05e9ecaf8a2c55473fb9e367220540c8a4a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:58:04 -0500 Subject: [PATCH 51/63] Remove stats updating from zmwatch.pl.in, where it shouldn't be --- scripts/zmwatch.pl.in | 87 +------------------------------------------ 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 5624e2372..80d66e5a8 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -79,91 +79,7 @@ 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 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->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() ); my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) or Fatal( "Can't execute: ".$sth->errstr() ); @@ -266,9 +182,8 @@ while( 1 ) { sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); } # end while (1) -$eventcounts_sth->finish(); - Info( "Watchdog exiting\n" ); exit(); + 1; __END__ From ae5f4c9d9c87c7f4f2b7929624e990a41b8a3a6d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:58:22 -0500 Subject: [PATCH 52/63] Add log re-init on HUP --- src/zma.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zma.cpp b/src/zma.cpp index 9f49cdd76..149f6961e 100644 --- a/src/zma.cpp +++ b/src/zma.cpp @@ -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 ); From 156bdbd089156c8c6836059e682040a2119399fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:59:27 -0500 Subject: [PATCH 53/63] Add zmstats, zmaudit, zmtrigger flags to Server object listing/editing --- web/skins/classic/views/options.php | 11 ++++++++++- web/skins/classic/views/server.php | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 2ca14f71d..56465ffa8 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -210,11 +210,17 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI + + + - + @@ -223,6 +229,9 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI + + + disabled="disabled"/> diff --git a/web/skins/classic/views/server.php b/web/skins/classic/views/server.php index 95c077ebf..46b4fd77b 100644 --- a/web/skins/classic/views/server.php +++ b/web/skins/classic/views/server.php @@ -58,6 +58,27 @@ xhtmlHeaders(__FILE__, translate('Server').' - '.$newServer['Name'] ); + + + + /> Yes + /> No + + + + + + /> Yes + /> No + + + + + + /> Yes + /> No + +
From 7d2becee2111c33bf7268f8ceb612d1a74ef6c16 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 12:59:42 -0500 Subject: [PATCH 54/63] add reload on HUP --- src/zmc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/zmc.cpp b/src/zmc.cpp index bafd4faf6..a58e04a86 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -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]; From 8186a17a86882a37daf535a51a47f98ed6df9e28 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 13:20:09 -0500 Subject: [PATCH 55/63] reduce frequency of Server updating --- scripts/zmdc.pl.in | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index ec8e8635f..1b1e05c67 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -285,20 +285,28 @@ sub run { 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 ) { From f14859d7d0f58fb5e039eb6f4852adeb931134ac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 13:20:24 -0500 Subject: [PATCH 56/63] Add ZM_STATS_UPDATE_INTERVAL --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 238eae2e7..923654b31 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -2367,6 +2367,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', From e605b48aa4c1844abc66cd36120f235196ae8fdb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 13:20:46 -0500 Subject: [PATCH 57/63] bump version to 1.31.20 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index eb3e9fedd..b4202e9fd 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.19 +1.31.20 From a410706455583c330afb5c049d7955eca908b746 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 13:35:10 -0500 Subject: [PATCH 58/63] add a new daemon to do stat updates --- scripts/zmstats.pl.in | 173 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 scripts/zmstats.pl.in diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in new file mode 100644 index 000000000..ae58b6fb9 --- /dev/null +++ b/scripts/zmstats.pl.in @@ -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__ From 64f9118e4681ebc2566c9e2e31be87a2aeb1f0cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 13:47:03 -0500 Subject: [PATCH 59/63] remove debug line --- web/skins/classic/views/options.php | 1 - 1 file changed, 1 deletion(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 56465ffa8..7bc563c85 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -219,7 +219,6 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI From eb2e78ccf70763c13df8cce728cd66b867ec58ca Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 14:33:21 -0500 Subject: [PATCH 60/63] Fix column names in triggers --- db/zm_update-1.31.20.sql | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/db/zm_update-1.31.20.sql b/db/zm_update-1.31.20.sql index 17d36bea6..4f0d9ee47 100644 --- a/db/zm_update-1.31.20.sql +++ b/db/zm_update-1.31.20.sql @@ -135,22 +135,22 @@ BEGIN 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 Events_Archived_Count = Events_Archived_Count+1, Events_Archived_DiskSpace = Events_Archived_DiskSpace + NEW.DiskSpace WHERE Id=NEW.MonitorId; + 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 Events_Archived_Count = Events_Archived_Count-1, Events_Archived_DiskSpace = Events_Archived_DiskSpace - OLD.DiskSpace WHERE Id=OLD.MonitorId; + 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 - Events_Archived_DiskSpace = Events_Archived_DiskSpace - OLD.DiskSpace + NEW.DiskSpace + 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 Events_Total_DiskSpace = Events_Total_DiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId; + UPDATE Monitors SET TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId; END IF; END; @@ -192,15 +192,15 @@ BEGIN IF ( OLD.Archived ) THEN DELETE FROM Events_Archived WHERE EventId=OLD.Id; UPDATE Monitors SET - Events_Archived = Events_Archived - 1, - Events_Archived_DiskSpace = Events_Archived_DiskSpace - OLD.DiskSpace, - Events_Total_Count = Events_Total_Count - 1, - Events_Total_DiskSpace = Events_Total_DiskSpace - OLD.DiskSpace + ArchivedEvents = ArchivedEvents - 1, + ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace, + TotalEvents = TotalEvents - 1, + TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace WHERE Id=OLD.MonitorId; ELSE UPDATE Monitors SET - Events_Total_Count = Events_Total_Count-1, - Events_Total_DiskSpace=Events_Total_DiskSpace-OLD.DiskSpace + TotalEvents = TotalEvents-1, + TotalEventDiskSpace=TotalEventDiskSpace-OLD.DiskSpace WHERE Id=OLD.MonitorId; END IF; END; From f5b8461a54c0e9dd3441e504273ccb2b94a1e983 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 14:33:31 -0500 Subject: [PATCH 61/63] Fix indexes on Groups_MOnitors --- db/zm_create.sql.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 9a8665933..934e77bdb 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -336,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` From a4d3b32b8cddfb550e7d776779892e790ec96f61 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 14:40:42 -0500 Subject: [PATCH 62/63] Update all the event counts in the Monitors so that Triggers can be acurate --- db/zm_update-1.31.20.sql | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/db/zm_update-1.31.20.sql b/db/zm_update-1.31.20.sql index 4f0d9ee47..358f15093 100644 --- a/db/zm_update-1.31.20.sql +++ b/db/zm_update-1.31.20.sql @@ -244,3 +244,32 @@ SET @s = (SELECT IF( 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; From f246599704c591fa3761503582fd905d35a15708 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 10 Jan 2018 14:56:12 -0500 Subject: [PATCH 63/63] Fix inserting default Server entry --- db/zm_create.sql.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 934e77bdb..e7e0ac4e3 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -701,7 +701,7 @@ CREATE TABLE `Storage` ( -- -- 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 */;