From f00e72be1e4e43b0d161520aea23a19c53e09859 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 21 Jan 2014 16:22:30 -0600 Subject: [PATCH] Initial commit for new zmcamtool.pl script --- distros/fedora/zoneminder.cmake.f19.spec | 1 + distros/fedora/zoneminder.f19.spec | 1 + distros/redhat/zoneminder.cmake.el6.spec | 1 + distros/redhat/zoneminder.el6.spec | 1 + scripts/CMakeLists.txt | 3 +- scripts/Makefile.am | 1 + scripts/zmcamtool.pl.in | 359 +++++++++++++++++++++++ 7 files changed, 366 insertions(+), 1 deletion(-) create mode 100755 scripts/zmcamtool.pl.in diff --git a/distros/fedora/zoneminder.cmake.f19.spec b/distros/fedora/zoneminder.cmake.f19.spec index d41d7233c..23e6327fc 100644 --- a/distros/fedora/zoneminder.cmake.f19.spec +++ b/distros/fedora/zoneminder.cmake.f19.spec @@ -143,6 +143,7 @@ fi %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl %{!?_without_x10:%{_bindir}/zmx10.pl} %{perl_vendorlib}/ZoneMinder* diff --git a/distros/fedora/zoneminder.f19.spec b/distros/fedora/zoneminder.f19.spec index b0733eb43..64a757c73 100644 --- a/distros/fedora/zoneminder.f19.spec +++ b/distros/fedora/zoneminder.f19.spec @@ -237,6 +237,7 @@ fi %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl %{!?_without_x10:%{_bindir}/zmx10.pl} %{perl_vendorlib}/ZoneMinder* diff --git a/distros/redhat/zoneminder.cmake.el6.spec b/distros/redhat/zoneminder.cmake.el6.spec index 6c26c6598..11b95d4bb 100644 --- a/distros/redhat/zoneminder.cmake.el6.spec +++ b/distros/redhat/zoneminder.cmake.el6.spec @@ -135,6 +135,7 @@ rm -rf %{_docdir}/%{name}-%{version} %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl %{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* diff --git a/distros/redhat/zoneminder.el6.spec b/distros/redhat/zoneminder.el6.spec index 63419b4d3..91ad69d68 100644 --- a/distros/redhat/zoneminder.el6.spec +++ b/distros/redhat/zoneminder.el6.spec @@ -244,6 +244,7 @@ fi %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl %{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index d78a4e56b..f9dd29f47 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(zmcamtool.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" @ONLY) if(NOT ZM_NO_X10) configure_file(zmx10.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" @ONLY) endif(NOT ZM_NO_X10) @@ -23,7 +24,7 @@ configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY) #configure_file(zmeventdump.in zmeventdump @ONLY) # 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" 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" 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) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 8f051f10b..f428c5fec 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -35,6 +35,7 @@ EXTRA_DIST = \ zmvideo.pl.in \ zmcontrol.pl.in \ zmtrack.pl.in \ + zmcamtool.pl.in \ ZoneMinder/Makefile.PL \ ZoneMinder/README \ ZoneMinder/Changes \ diff --git a/scripts/zmcamtool.pl.in b/scripts/zmcamtool.pl.in new file mode 100755 index 000000000..c8f23f731 --- /dev/null +++ b/scripts/zmcamtool.pl.in @@ -0,0 +1,359 @@ +#!/usr/bin/perl -w +# +# ========================================================================== +# +# ZoneMinder Update 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ========================================================================== +# +# This script provides a way to import new ptz camera controls & camera presets +# into existing zoneminder systems. This script also provides a way to export +# ptz camera controls & camera presets from an existing zoneminder system into +# a sql file, which can then be easily imported to another zoneminder system. +# +use strict; +use bytes; + +@EXTRA_PERL_LIB@ +use ZoneMinder::Base qw(:all); +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::General qw(:all); +use ZoneMinder::Database qw(:all); +use ZoneMinder::ConfigAdmin qw( :functions ); +use POSIX; +use DBI; +use Getopt::Long; +use Data::Dumper; + +# TO-DO - Figure out if "detainting" the environment is really necessary for this script +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +# TO-DO - Decide what, if anything, should be passed to the zoneminder logger +my $web_uid = (getpwnam( $Config{ZM_WEB_USER} ))[2]; +my $use_log = (($> == 0) || ($> == $web_uid)); + +logInit( toFile=>$use_log?DEBUG:NOLOG ); +logSetSignal(); + +my $export = 0; +my $import = 0; +my $overwrite = 0; +my $help = 0; +my $topreset = 0; +my $sqlfile = ''; +my $dbUser = $Config{ZM_DB_USER}; +my $dbPass = $Config{ZM_DB_PASS}; + +# Process commandline parameters with getopt long +if ( !GetOptions( 'export'=>\$export, 'import'=>\$import, 'overwrite'=>\$overwrite, 'help'=>\$help, 'topreset'=>\$topreset, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass ) ) { + Usage(); +} + +$Config{ZM_DB_USER} = $dbUser; +$Config{ZM_DB_PASS} = $dbPass; + +# Check to make sure commandline params make sense +if ( ((!$help) && ($import + $export + $topreset) != 1 )) { + print( STDERR qq/Please give only one of the following: "import", "export", or "topreset".\n/ ); + Usage(); +} + +if ( ($export)&&($overwrite) ) { + print "Warning: Overwrite parameter ignored during an export.\n"; +} + +# Call the appropriate subroutine based on the params given on the commandline +if ($help) { + Usage(); +} + +if ($export) { + exportsql(); +} + +if ($import) { + importsql(); +} + +if ($topreset) { + toPreset(); +} + +############### +# SUBROUTINES # +############### + +# Usage subroutine help text +sub Usage +{ +die(" +USAGE: +zmcamtool.pl [--user= --pass=] + [[--overwrite] --import [file.sql]] + [--export [name]] + [--topreset monitor id] + +PARAMETERS: +--export - Export all camera controls and presets to STDOUT. + Optionally specify a control or preset name. +--import - Import new camera controls and presets found in + zm_create.sql into the ZoneMinder dB. + Optionally specify an alternate sql file to read from. +--topreset - Copy a monitor to a Camera Preset given the monitor id. +--overwrite - Overwrite any existing controls or presets. + with the same name as the new controls or presets. +--help - Print usage information +--user= - Alternate dB user with privileges to alter dB. +--pass= - Password of alternate dB user with privileges to alter dB. +\n"); +} + +# Subroutine that checks if a name already exists in a sql table +# Not currently in-use b/c it requires a call to zmdbconnect each time (expensive) +sub checkName +{ + my $sqltable = shift; + my $sqlname = shift; + my $result = 0; + + my $dbh = zmDbConnect(); + my $sql = "select count(*) from $sqltable where Name = ?"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($sqlname) or die( "Can't execute: ".$sth->errstr() ); + + my $rows = $sth->fetchrow_arrayref(); + $sth->finish(); + + if ($rows->[0] > 0) { + $result = 1; + } + + return $result; +} + +# Subroutine to import camera control & presets into the zoneminder dB +sub importsql +{ + my @newcontrols; + my @overwritecontrols; + my @skippedcontrols; + my @newpresets; + my @overwritepresets; + my @skippedpresets; + my %controls; + my %monitorpresets; + + if ($ARGV[0]) { + $sqlfile = $ARGV[0]; + } else { + $sqlfile = $Config{ZM_PATH_DATA}.'/db/zm_create.sql'; + } + + open(SQLFILE, $sqlfile) or die "Can't Open file: $!\n"; + + # Find and extract ptz control and monitor preset records + while () { + # Our regex replaces the primary key with NULL + if (s/^(INSERT INTO .*?Controls.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { + $controls{$3} = $_; + } elsif (s/^(INSERT INTO .*?MonitorPresets.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { + $monitorpresets{$3} = $_; + } + } + close SQLFILE; + + if ( ! (%controls || %monitorpresets) ) { + die "Error: No relevant data found in $sqlfile.\n"; + } + + # Now that we've got what we were looking for, compare to what is already in the dB + + my $dbh = zmDbConnect(); + foreach (keys %controls) { + my $sql = "select count(*) from Controls where Name = ?"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($_) or die( "Can't execute: ".$sth->errstr() ); + + my $rows = $sth->fetchrow_arrayref(); + $sth->finish(); + + if ($rows->[0] == 0) { + # No existing Control was found. Add new control to dB. + $sql = $controls{$_}; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + push @newcontrols, $_; + } elsif ($overwrite) { + # An existing Control was found and the overwrite flag is set. Overwrite the control. + $sql = "delete from Controls where Name = ?"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute($_) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + $sql = $controls{$_}; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + push @overwritecontrols, $_; + } else { + # An existing Control was found and the overwrite flag was not set. Do nothing. + push @skippedcontrols, $_; + } + $sth->finish(); + } + + foreach (keys %monitorpresets) { + my $sql = "select count(*) from MonitorPresets where Name = ?"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($_) or die( "Can't execute: ".$sth->errstr() ); + + my $rows = $sth->fetchrow_arrayref(); + $sth->finish(); + + if ($rows->[0] == 0) { + # No existing MonitorPreset was found. Add new MonitorPreset to dB. + $sql = $monitorpresets{$_}; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + push @newpresets, $_; + } elsif ($overwrite) { + # An existing MonitorPreset was found and the overwrite flag is set. Overwrite the MonitorPreset. + $sql = "delete from MonitorPresets where Name = ?"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute($_) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + $sql = $monitorpresets{$_}; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + push @overwritepresets, $_; + } else { + # An existing MonitorPreset was found and the overwrite flag was not set. Do nothing. + push @skippedpresets, $_; + } + $sth->finish(); + } + + if (@newcontrols) { + print "Number of ptz camera controls added: ".scalar(@newcontrols)."\n"; + } + if (@overwritecontrols) { + print "Number of existing ptz camera controls overwritten: ".scalar(@overwritecontrols)."\n"; + } + if (@skippedcontrols) { + print "Number of existing ptz camera controls skipped: ".scalar(@skippedcontrols)."\n"; + } + + if (@newpresets) { + print "Number of monitor presets added: ".scalar(@newpresets)."\n"; + } + if (@overwritepresets) { + print "Number of existing monitor presets overwritten: ".scalar(@overwritepresets)."\n"; + } + if (@skippedpresets) { + print "Number of existing presets skipped: ".scalar(@skippedpresets)."\n"; + } +} + +# Subroutine to export camera control & presets from the zoneminder dB to STDOUT +sub exportsql +{ + +my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); +my $command = "mysqldump -t --skip-opt --compact -h".$host; +$command .= " -P".$port if defined($port); +if ( $dbUser ) { + $command .= " -u".$dbUser; + if ( $dbPass ) { + $command .= " -p".$dbPass; + } + } + +if ($ARGV[0]) { + $command .= qq( --where="Name = '$ARGV[0]'"); +} + +$command .= " zm Controls MonitorPresets"; + +my $output = qx($command); +my $status = $? >> 8; +# TO-DO - This does not seem necessary +#if ( $status || logDebugging() ) { +# chomp( $output ); +# print( "Output: $output\n" ); +#} +if ( $status ) { + die( "Command '$command' exited with status: $status\n" ); + } else { + # NULLify the primary keys before printing the output to STDOUT + $output =~ s/VALUES \((.*?),'/VALUES \(NULL,'/ig; + print $output; + } +} + +sub toPreset +{ + my $dbh = zmDbConnect(); + my $monitorid = $ARGV[0]; + my @data; + + # Grap the appropriate fields from the Monitors table + my $sql = "select Name, Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, SubPath, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale from Monitors where Id = ?"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($monitorid) or die( "Can't execute: ".$sth->errstr() ); + + @data = $sth->fetchrow_array(); + $sth->finish(); + + # + # TO-DO - Add code that tries to intelligently replace certain values with generic ones e.g. , , , etc. + # + + # Check that we don't already have a record with the same name +# my $nameexists = checkName("MonitorPresets",$data[0]); + + $sql = "select count(*) from MonitorPresets where Name = ?"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute($data[0]) or die( "Can't execute: ".$sth->errstr() ); + + my $rows = $sth->fetchrow_arrayref(); + $sth->finish(); + + if ($rows->[0] == 0) { + # No existing Preset was found. Add new Preset to dB. + print "Adding new preset: $data[0]\n"; + $sql = "insert into MonitorPresets values (NULL,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute(@data) or die( "Can't execute: ".$sth->errstr() ); + } elsif ($overwrite) { + # An existing Control was found and the overwrite flag is set. Overwrite the control. + print "Existing preset $data[0] dedected.\nOverwriting...\n"; + $sql = "delete from MonitorPresets where Name = ?"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute($data[0]) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + $sql = "insert into MonitorPresets values (NULL,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute(@data) or die( "Can't execute: ".$sth->errstr() ); + } else { + # An existing Control was found and the overwrite flag was not set. Do nothing. + print "Existing preset $data[0] detected and overwrite flag not set.\nSkipping...\n"; + } + $sth->finish(); +} +