#!/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 sub checkName { my $dbh = shift; my $sqltable = shift; my $sqlname = shift; my $result = 0; 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 that deletes a name from a sql table sub rmName { my $dbh = shift; my $sqltable = shift; my $sqlname = shift; my $sql = "delete 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() ); $sth->finish(); return $res; } # 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) { if (!checkName($dbh,"Controls",$_)) { # No existing Control was found. Add new control to dB. my $sql = $controls{$_}; 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() ); push @newcontrols, $_; } elsif ($overwrite) { # An existing Control was found and the overwrite flag is set. Overwrite the control. rmName($dbh,"Controls",$_); my $sql = $controls{$_}; 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() ); $sth->finish(); push @overwritecontrols, $_; } else { # An existing Control was found and the overwrite flag was not set. Do nothing. push @skippedcontrols, $_; } } foreach (keys %monitorpresets) { if (!checkName($dbh,"MonitorPresets",$_)) { # No existing MonitorPreset was found. Add new MonitorPreset to dB. my $sql = $monitorpresets{$_}; 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() ); push @newpresets, $_; } elsif ($overwrite) { # An existing MonitorPreset was found and the overwrite flag is set. Overwrite the MonitorPreset. rmName($dbh,"MonitorPresets",$_); my $sql = $monitorpresets{$_}; 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() ); $sth->finish(); push @overwritepresets, $_; } else { # An existing MonitorPreset was found and the overwrite flag was not set. Do nothing. push @skippedpresets, $_; } } 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. # if (!checkName($dbh,"MonitorPresets",$data[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(); }