Merge branch 'master' of https://github.com/ZoneMinder/ZoneMinder into plugin_support_list

Conflicts:
	scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
This commit is contained in:
Emmanuel Papin 2015-05-14 11:12:38 +02:00
commit 6b2c435460
46 changed files with 5720 additions and 3192 deletions

View File

@ -107,6 +107,8 @@ if [ $1 -eq 0 ] ; then
# Package removal, not upgrade # Package removal, not upgrade
/bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || :
/bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || :
echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n"
/usr/sbin/semodule -r local_zoneminder.pp
fi fi
%postun %postun
@ -128,7 +130,8 @@ fi
%files %files
%defattr(-,root,root,-) %defattr(-,root,root,-)
%doc AUTHORS COPYING README.md distros/redhat/README.Centos7 distros/redhat/jscalendar-doc %doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.Centos7 distros/redhat/jscalendar-doc
%doc distros/redhat/cambozola-doc distros/redhat/local_zoneminder.te
%config %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf %config %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf
%config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf %config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf
%config(noreplace) /etc/tmpfiles.d/zoneminder.conf %config(noreplace) /etc/tmpfiles.d/zoneminder.conf
@ -156,7 +159,8 @@ fi
%{_bindir}/zmx10.pl %{_bindir}/zmx10.pl
%{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ZoneMinder* %{perl_vendorarch}/auto/ZoneMinder/.packlist
#%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ZoneMinder*
#%{perl_archlib}/ZoneMinder* #%{perl_archlib}/ZoneMinder*
%{_mandir}/man*/* %{_mandir}/man*/*
%dir %{_libexecdir}/zoneminder %dir %{_libexecdir}/zoneminder

View File

@ -36,7 +36,15 @@ use ZoneMinder::General qw(:all);
use ZoneMinder::Database qw(:all); use ZoneMinder::Database qw(:all);
use ZoneMinder::Memory qw(:all); use ZoneMinder::Memory qw(:all);
our @ISA = qw(Exporter ZoneMinder::Base ZoneMinder::Config ZoneMinder::Logger ZoneMinder::General ZoneMinder::Database ZoneMinder::Memory); our @ISA = qw(
Exporter
ZoneMinder::Base
ZoneMinder::Config
ZoneMinder::Logger
ZoneMinder::General
ZoneMinder::Database
ZoneMinder::Memory
);
# Items to export into callers namespace by default. Note: do not export # Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead. # names by default without a very good reason. Use EXPORT_OK instead.

View File

@ -62,7 +62,11 @@ ZoneMinder::Base - Base perl module for ZoneMinder
=head1 DESCRIPTION =head1 DESCRIPTION
This module is the base module for the rest of the ZoneMinder modules. It is included by each of the other modules but serves no purpose other than to propagate the perl module version amongst the other modules. You will never need to use this module directly but if you write new ZoneMinder modules they should include it. This module is the base module for the rest of the ZoneMinder modules. It
is included by each of the other modules but serves no purpose other than
to propagate the perl module version amongst the other modules. You will
never need to use this module directly but if you write new ZoneMinder
modules they should include it.
=head2 EXPORT =head2 EXPORT

View File

@ -89,7 +89,11 @@ BEGIN
close( $CONFIG ); close( $CONFIG );
use DBI; use DBI;
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ) or croak( "Can't connect to db" ); my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
) or croak( "Can't connect to db" );
my $sql = 'select * from Config'; my $sql = 'select * from Config';
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() );
@ -113,9 +117,16 @@ ZoneMinder::Config - ZoneMinder configuration module.
=head1 DESCRIPTION =head1 DESCRIPTION
The ZoneMinder::Config module is used to import the ZoneMinder configuration from the database. It will do this at compile time in a BEGIN block and require access to the zm.conf file either in the current directory or in its defined location in order to determine database access details, configuration from this file will also be included. If the :all or :config tags are used then this configuration is exported into the namespace of the calling program or module. The ZoneMinder::Config module is used to import the ZoneMinder
configuration from the database. It will do this at compile time in a BEGIN
block and require access to the zm.conf file either in the current
directory or in its defined location in order to determine database access
details, configuration from this file will also be included. If the :all or
:config tags are used then this configuration is exported into the
namespace of the calling program or module.
Once the configuration has been imported then configuration variables are defined as constants and can be accessed directory by name, e.g. Once the configuration has been imported then configuration variables are
defined as constants and can be accessed directory by name, e.g.
$lang = $Config{ZM_LANG_DEFAULT}; $lang = $Config{ZM_LANG_DEFAULT};

View File

@ -68,7 +68,11 @@ use Carp;
sub loadConfigFromDB sub loadConfigFromDB
{ {
print( "Loading config from DB\n" ); print( "Loading config from DB\n" );
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
,$Config{ZM_DB_USER}
,$Config{ZM_DB_PASS}
);
if ( !$dbh ) if ( !$dbh )
{ {
@ -76,8 +80,10 @@ sub loadConfigFromDB
return( 0 ); return( 0 );
} }
my $sql = "select * from Config"; my $sql = "select * from Config";
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or croak( "Can't execute: ".$sth->errstr() );
my $option_count = 0; my $option_count = 0;
while( my $config = $sth->fetchrow_hashref() ) while( my $config = $sth->fetchrow_hashref() )
{ {
@ -111,7 +117,11 @@ sub loadConfigFromDB
sub saveConfigToDB sub saveConfigToDB
{ {
print( "Saving config to DB\n" ); print( "Saving config to DB\n" );
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
,$Config{ZM_DB_USER}
,$Config{ZM_DB_PASS}
);
if ( !$dbh ) if ( !$dbh )
{ {
@ -122,13 +132,16 @@ sub saveConfigToDB
my $ac = $dbh->{AutoCommit}; my $ac = $dbh->{AutoCommit};
$dbh->{AutoCommit} = 0; $dbh->{AutoCommit} = 0;
$dbh->do('LOCK TABLE Config WRITE') or croak( "Can't lock Config table: " . $dbh->errstr() ); $dbh->do('LOCK TABLE Config WRITE')
or croak( "Can't lock Config table: " . $dbh->errstr() );
my $sql = "delete from Config"; my $sql = "delete from Config";
my $res = $dbh->do( $sql ) or croak( "Can't do '$sql': ".$dbh->errstr() ); my $res = $dbh->do( $sql )
or croak( "Can't do '$sql': ".$dbh->errstr() );
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?"; $sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?";
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
foreach my $option ( @options ) foreach my $option ( @options )
{ {
#next if ( $option->{category} eq 'hidden' ); #next if ( $option->{category} eq 'hidden' );
@ -139,7 +152,10 @@ sub saveConfigToDB
$option->{db_format} = $option->{type}->{format}; $option->{db_format} = $option->{type}->{format};
if ( $option->{db_type} eq "boolean" ) if ( $option->{db_type} eq "boolean" )
{ {
$option->{db_value} = ($option->{value} eq "yes")?"1":"0"; $option->{db_value} = ($option->{value} eq "yes")
? "1"
: "0"
;
} }
else else
{ {
@ -147,12 +163,35 @@ sub saveConfigToDB
} }
if ( my $requires = $option->{requires} ) if ( my $requires = $option->{requires} )
{ {
$option->{db_requires} = join( ";", map { my $value = $_->{value}; $value = ($value eq "yes")?1:0 if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ); ( "$_->{name}=$value" ) } @$requires ); $option->{db_requires} = join( ";",
map {
my $value = $_->{value};
$value = ($value eq "yes")
? 1
: 0
if ( $options_hash{$_->{name}}->{db_type} eq "boolean" )
; ( "$_->{name}=$value" )
} @$requires
);
} }
else else
{ {
} }
my $res = $sth->execute( $option->{id}, $option->{name}, $option->{db_value}, $option->{db_type}, $option->{default}, $option->{db_hint}, $option->{db_pattern}, $option->{db_format}, $option->{description}, $option->{help}, $option->{category}, $option->{readonly}?1:0, $option->{db_requires} ) or croak( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute(
$option->{id},
$option->{name},
$option->{db_value},
$option->{db_type},
$option->{default},
$option->{db_hint},
$option->{db_pattern},
$option->{db_format},
$option->{description},
$option->{help},
$option->{category},
$option->{readonly} ? 1 : 0,
$option->{db_requires}
) or croak( "Can't execute: ".$sth->errstr() );
} }
$sth->finish(); $sth->finish();
@ -179,9 +218,15 @@ ZoneMinder::ConfigAdmin - ZoneMinder Configuration Administration module
=head1 DESCRIPTION =head1 DESCRIPTION
The ZoneMinder:ConfigAdmin module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is intended for specialist confguration management and would not normally be used by end users. The ZoneMinder:ConfigAdmin module contains the master definition of the
ZoneMinder configuration options as well as helper methods. This module is
intended for specialist confguration management and would not normally be
used by end users.
The configuration held in this module, which was previously in zmconfig.pl, includes the name, default value, description, help text, type and category for each option, as well as a number of additional fields in a small number of cases. The configuration held in this module, which was previously in zmconfig.pl,
includes the name, default value, description, help text, type and category
for each option, as well as a number of additional fields in a small number
of cases.
=head1 METHODS =head1 METHODS
@ -189,11 +234,17 @@ The configuration held in this module, which was previously in zmconfig.pl, incl
=item loadConfigFromDB (); =item loadConfigFromDB ();
Loads existing configuration from the database (if any) and merges it with the definitions held in this module. This results in the merging of any new configuration and the removal of any deprecated configuration while preserving the existing values of every else. Loads existing configuration from the database (if any) and merges it with
the definitions held in this module. This results in the merging of any new
configuration and the removal of any deprecated configuration while
preserving the existing values of every else.
=item saveConfigToDB (); =item saveConfigToDB ();
Saves configuration held in memory to the database. The act of loading and saving configuration is a convenient way to ensure that the configuration held in the database corresponds with the most recent definitions and that all components are using the same set of configuration. Saves configuration held in memory to the database. The act of loading and
saving configuration is a convenient way to ensure that the configuration
held in the database corresponds with the most recent definitions and that
all components are using the same set of configuration.
=back =back

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ our @ISA = qw(ZoneMinder::Control);
# of the position of your mouse on the arrow. # of the position of your mouse on the arrow.
# Extremity of arrow equal to fastest speed of movement # Extremity of arrow equal to fastest speed of movement
# Close the base of arrow to lowest speed of movement # Close the base of arrow to lowest speed of movement
# for diagonaly you can click before the begining of the arrow for low speed # for diagonaly you can click before the beginning of the arrow for low speed
# In round center equal to stop to move and switch of latest OSD # In round center equal to stop to move and switch of latest OSD
# -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted)
# -Zoom Tele switch ON InfraRed LED and stay to manual IR MODE # -Zoom Tele switch ON InfraRed LED and stay to manual IR MODE
@ -63,7 +63,7 @@ our @ISA = qw(ZoneMinder::Control);
# -8 Preset PTZ are implemented and functionnal # -8 Preset PTZ are implemented and functionnal
# -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section
# -This script is compatible with the basic authentification method used by mostly new camera based with hi3510 chipset # -This script is compatible with the basic authentification method used by mostly new camera based with hi3510 chipset
# -AutoStop function is active and you must set up value (in sec exemple 0.7) under AutoStop section # -AutoStop function is active and you must set up value (in sec example 0.7) under AutoStop section
# or you can set up to 0 for disable it (in this case you need to click to the circle center for stop) # or you can set up to 0 for disable it (in this case you need to click to the circle center for stop)
# -"White In" to control Brightness, "auto" for restore the original value of Brightness # -"White In" to control Brightness, "auto" for restore the original value of Brightness
# -"White Out" to control Contrast, "man" for restore the original value of Contrast # -"White Out" to control Contrast, "man" for restore the original value of Contrast

View File

@ -57,7 +57,7 @@ our @ISA = qw(ZoneMinder::Control);
# of the position of your mouse on the arrow. # of the position of your mouse on the arrow.
# Extremity of arrow equal to fastest speed of movement # Extremity of arrow equal to fastest speed of movement
# Close the base of arrow to lowest speed of movement # Close the base of arrow to lowest speed of movement
# for diagonaly you can click before the begining of the arrow for low speed # for diagonaly you can click before the beginning of the arrow for low speed
# In round center equal to stop to move # In round center equal to stop to move
# -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted)
# -Zoom Tele/Wide with time control to simulate speed because speed value do not work (buggy firmware or not implemented on this cam) # -Zoom Tele/Wide with time control to simulate speed because speed value do not work (buggy firmware or not implemented on this cam)
@ -67,7 +67,7 @@ our @ISA = qw(ZoneMinder::Control);
# You Need to configure ZoneMinder PANSPEED & TILTSEPPED & ZOOMSPEED 1 to 63 by 1 step # You Need to configure ZoneMinder PANSPEED & TILTSEPPED & ZOOMSPEED 1 to 63 by 1 step
# -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section
# -This script is compatible with the basic authentification method used by mostly new camera # -This script is compatible with the basic authentification method used by mostly new camera
# -AutoStop function is active and you must set up value (in sec exemple 0.5) under AutoStop section # -AutoStop function is active and you must set up value (in sec example 0.5) under AutoStop section
# or you can set up to 0 for disable it but the camera never stop to move and trust me, she can move all the night... # or you can set up to 0 for disable it but the camera never stop to move and trust me, she can move all the night...
# (you need to click to the center arrow for stop) # (you need to click to the center arrow for stop)
# -"White In" to control Brightness, "auto" for restore the original value of Brightness # -"White In" to control Brightness, "auto" for restore the original value of Brightness

View File

@ -226,10 +226,11 @@ ZoneMinder::Control::FI8908W - Foscam FI8908W camera control
=head1 DESCRIPTION =head1 DESCRIPTION
This module contains the implementation of the Foscam FI8908W / FI8918W IP camera control This module contains the implementation of the Foscam FI8908W / FI8918W IP
protocol. camera control protocol.
The module uses "Control Device" value to retrieve user and password. User
and password should be separated by colon, e.g. user:password. If colon is
not provided, then "admin" is used as a fallback value for the user.
The module uses "Control Device" value to retrieve user and password. User and password should
be separated by colon, e.g. user:password. If colon is not provided, then "admin" is used
as a fallback value for the user.
=cut =cut

View File

@ -102,7 +102,6 @@ sub close
sub printMsg sub printMsg
{ {
my $self = shift;
my $msg = shift; my $msg = shift;
my $msg_len = length($msg); my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" ); Debug( $msg."[".$msg_len."]" );
@ -113,9 +112,28 @@ sub sendCmd
my $self = shift; my $self = shift;
my $cmd = shift; my $cmd = shift;
my $result = undef; my $result = undef;
my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice};
if ( ! $password ) {
$password = $user;
$user = 'admin';
}
$user = 'admin' if ! $user;
$password = 'pwd' if ! $password;
$cmd .= "&usr=$user&pwd=$password";
printMsg( $cmd, "Tx" ); printMsg( $cmd, "Tx" );
my $temps = time(); my $url;
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?usr%3Dadmin%26pwd%3D".$self->{Monitor}->{ControlDevice}."%26cmd%3D".$cmd."%26".$temps ); if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress};
} else {
$url = "http://".$self->{Monitor}->{ControlAddress};
}
$url .= "/cgi-bin/CGIProxy.fcgi?cmd=$cmd%26".time;
printMsg( $url, "Tx" );
my $req = HTTP::Request->new( GET=>$url );
my $res = $self->{ua}->request($req); my $res = $self->{ua}->request($req);
if ( $res->is_success ) if ( $res->is_success )
{ {
@ -134,7 +152,7 @@ sub reset
# Setup OSD # Setup OSD
my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0"; my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0";
$self->sendCmd( $cmd ); $self->sendCmd( $cmd );
# Setup For Stream=0 Resolution=720p Bandwith=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON # Setup For Stream=0 Resolution=720p Bandwidth=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON
$cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1"; $cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1";
$self->sendCmd( $cmd ); $self->sendCmd( $cmd );
# Setup For Infrared AUTO # Setup For Infrared AUTO

View File

@ -110,9 +110,13 @@ sub sendCmd {
my $url; my $url;
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time; $url = $self->{Monitor}->{ControlAddress}
.'/cgi-bin/setGPIO.cgi?preventCache='.time
;
} else { } else {
$url = 'http://'.$self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time; $url = 'http://'.$self->{Monitor}->{ControlAddress}
.'/cgi-bin/setGPIO.cgi?preventCache='.time
;
} # en dif } # en dif
Error("Url: $url $cmd"); Error("Url: $url $cmd");
my $uri = URI::Encode->new( { encode_reserved => 0 } ); my $uri = URI::Encode->new( { encode_reserved => 0 } );
@ -203,7 +207,11 @@ sub moveMap
my $xcoord = $self->getParam( $params, 'xcoord' ); my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' ); my $ycoord = $self->getParam( $params, 'ycoord' );
Debug( "Move Map to $xcoord,$ycoord" ); Debug( "Move Map to $xcoord,$ycoord" );
my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}."&imageheight=".$self->{Monitor}->{Height}; my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth="
.$self->{Monitor}->{Width}
."&imageheight="
.$self->{Monitor}->{Height}
;
$self->sendCmd( $cmd ); $self->sendCmd( $cmd );
} }

View File

@ -111,7 +111,8 @@ sub open
my $self = shift; my $self = shift;
$self->loadMonitor(); $self->loadMonitor();
my ( $protocol, $username, $password, $address ) = $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/; my ( $protocol, $username, $password, $address )
= $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/;
if ( $username ) { if ( $username ) {
$USERNAME = $username; $USERNAME = $username;
$PASSWORD = $password; $PASSWORD = $password;
@ -130,7 +131,12 @@ sub open
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION ); $self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open'; $self->{state} = 'open';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string) # credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug ( "sendCmd credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'"); Debug ( "sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
} }

View File

@ -83,11 +83,20 @@ sub zmDbConnect
if ( defined($port) ) if ( defined($port) )
{ {
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$host.";port=".$port, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$host
.";port=".$port
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);
} }
else else
{ {
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);
} }
$dbh->trace( 0 ); $dbh->trace( 0 );
} }
@ -140,8 +149,10 @@ sub zmDbGetMonitors
$sql .= " where Function = 'Nodect'"; $sql .= " where Function = 'Nodect'";
} }
} }
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute() or croak( "Can't execute '$sql': ".$sth->errstr() ); or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or croak( "Can't execute '$sql': ".$sth->errstr() );
my @monitors; my @monitors;
while( my $monitor = $sth->fetchrow_hashref() ) while( my $monitor = $sth->fetchrow_hashref() )
@ -161,8 +172,10 @@ sub zmDbGetMonitor
return( undef ) if ( !defined($id) ); return( undef ) if ( !defined($id) );
my $sql = "select * from Monitors where Id = ?"; my $sql = "select * from Monitors where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute( $id ) or croak( "Can't execute '$sql': ".$sth->errstr() ); or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $id )
or croak( "Can't execute '$sql': ".$sth->errstr() );
my $monitor = $sth->fetchrow_hashref(); my $monitor = $sth->fetchrow_hashref();
return( $monitor ); return( $monitor );
@ -176,9 +189,15 @@ sub zmDbGetMonitorAndControl
return( undef ) if ( !defined($id) ); return( undef ) if ( !defined($id) );
my $sql = "select C.*,M.*,C.Protocol from Monitors as M inner join Controls as C on (M.ControlId = C.Id) where M.Id = ?"; my $sql = "SELECT C.*,M.*,C.Protocol
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); FROM Monitors as M
my $res = $sth->execute( $id ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); INNER JOIN Controls as C on (M.ControlId = C.Id)
WHERE M.Id = ?"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $id )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $monitor = $sth->fetchrow_hashref(); my $monitor = $sth->fetchrow_hashref();
return( $monitor ); return( $monitor );

View File

@ -203,13 +203,26 @@ sub getEventPath
my $event_path = ""; my $event_path = "";
if ( $Config{ZM_USE_DEEP_STORAGE} ) if ( $Config{ZM_USE_DEEP_STORAGE} )
{ {
$event_path = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d/%H/%M/%S", localtime($event->{Time}) ); $event_path = $Config{ZM_DIR_EVENTS}
.'/'.$event->{MonitorId}
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
localtime($event->{Time})
)
;
} }
else else
{ {
$event_path = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.$event->{Id}; $event_path = $Config{ZM_DIR_EVENTS}
.'/'.$event->{MonitorId}
.'/'.$event->{Id}
;
}
if ( index($Config{ZM_DIR_EVENTS},'/') != 0 ){
$event_path = $Config{ZM_PATH_WEB}
.'/'.$event_path
;
} }
$event_path = $Config{ZM_PATH_WEB}.'/'.$event_path if ( index($Config{ZM_DIR_EVENTS},'/') != 0 );
return( $event_path ); return( $event_path );
} }
@ -219,7 +232,9 @@ sub createEventPath
# WARNING assumes running from events directory # WARNING assumes running from events directory
# #
my $event = shift; my $event = shift;
my $eventRootPath = ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); my $eventRootPath = ($Config{ZM_DIR_EVENTS}=~m|/|)
? $Config{ZM_DIR_EVENTS}
: ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS});
my $eventPath = $eventRootPath.'/'.$event->{MonitorId}; my $eventPath = $eventRootPath.'/'.$event->{MonitorId};
if ( $Config{ZM_USE_DEEP_STORAGE} ) if ( $Config{ZM_USE_DEEP_STORAGE} )
@ -242,7 +257,8 @@ sub createEventPath
# Create event id symlink # Create event id symlink
my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
symlink( $timePath, $idFile ) or Fatal( "Can't symlink $idFile -> $eventPath: $!" ); symlink( $timePath, $idFile )
or Fatal( "Can't symlink $idFile -> $eventPath: $!" );
makePath( $timePath, $eventPath ); makePath( $timePath, $eventPath );
$eventPath .= '/'.$timePath; $eventPath .= '/'.$timePath;
@ -281,8 +297,13 @@ sub _checkProcessOwner
my ( $processOwner ) = getpwuid( $> ); my ( $processOwner ) = getpwuid( $> );
if ( $processOwner ne $Config{ZM_WEB_USER} ) if ( $processOwner ne $Config{ZM_WEB_USER} )
{ {
# Not running as web user, so should be root in whch case chown the temporary directory # Not running as web user, so should be root in which case chown
( my $ownerName, my $ownerPass, $_ownerUid, $_ownerGid ) = getpwnam( $Config{ZM_WEB_USER} ) or Fatal( "Can't get user details for web user '".$Config{ZM_WEB_USER}."': $!" ); # the temporary directory
( my $ownerName, my $ownerPass, $_ownerUid, $_ownerGid )
= getpwnam( $Config{ZM_WEB_USER} )
or Fatal( "Can't get user details for web user '"
.$Config{ZM_WEB_USER}."': $!"
);
$_setFileOwner = 1; $_setFileOwner = 1;
} }
else else
@ -299,7 +320,10 @@ sub setFileOwner
if ( _checkProcessOwner() ) if ( _checkProcessOwner() )
{ {
chown( $_ownerUid, $_ownerGid, $file ) or Fatal( "Can't change ownership of file '$file' to '".$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" ); chown( $_ownerUid, $_ownerGid, $file )
or Fatal( "Can't change ownership of file '$file' to '"
.$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!"
);
} }
} }
@ -337,9 +361,14 @@ sub createEvent
elsif ( $event->{MonitorId} ) elsif ( $event->{MonitorId} )
{ {
my $sql = "select * from Monitors where Id = ?"; my $sql = "select * from Monitors where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
$event->{monitor} = $sth->fetchrow_hashref() or Fatal( "Unable to create event, can't load monitor with id '".$event->{MonitorId}."'" ); my $res = $sth->execute( $event->{MonitorId} )
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
$event->{monitor} = $sth->fetchrow_hashref()
or Fatal( "Unable to create event, can't load monitor with id '"
.$event->{MonitorId}."'"
);
$sth->finish(); $sth->finish();
} }
else else
@ -360,7 +389,9 @@ sub createEvent
my $imageInfo = Image::Info::image_info( $frame->{imagePath} ); my $imageInfo = Image::Info::image_info( $frame->{imagePath} );
if ( $imageInfo->{error} ) if ( $imageInfo->{error} )
{ {
Error( "Unable to extract image info from '".$frame->{imagePath}."': ".$imageInfo->{error} ); Error( "Unable to extract image info from '"
.$frame->{imagePath}."': ".$imageInfo->{error}
);
} }
else else
{ {
@ -396,18 +427,25 @@ sub createEvent
push( @values, $event->{$field} ); push( @values, $event->{$field} );
} }
my $sql = "insert into Events (".join(',',@fields).") values (".join(',',@formats).")"; my $sql = "INSERT INTO Events (".join(',',@fields)
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); .") VALUES (".join(',',@formats).")"
my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); ;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
my $res = $sth->execute( @values )
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
$event->{Id} = $dbh->{mysql_insertid}; $event->{Id} = $dbh->{mysql_insertid};
Info( "Created event ".$event->{Id} ); Info( "Created event ".$event->{Id} );
if ( $event->{EndTime} ) if ( $event->{EndTime} )
{ {
$event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id}
if ( $event->{Name} eq 'New Event' );
my $sql = "update Events set Name = ? where Id = ?"; my $sql = "update Events set Name = ? where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute( $event->{Name}, $event->{Id} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Name}, $event->{Id} )
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
} }
my $eventPath = createEventPath( $event ); my $eventPath = createEventPath( $event );
@ -430,19 +468,39 @@ sub createEvent
push( @values, $frame->{$field} ); push( @values, $frame->{$field} );
} }
my $sql = "insert into Frames (".join(',',@fields).") values (".join(',',@formats).")"; my $sql = "insert into Frames (".join(',',@fields)
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); .") values (".join(',',@formats).")"
my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); ;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
my $res = $sth->execute( @values )
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
#$frame->{FrameId} = $dbh->{mysql_insertid}; #$frame->{FrameId} = $dbh->{mysql_insertid};
if ( $frame->{imagePath} ) if ( $frame->{imagePath} )
{ {
$frame->{capturePath} = sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", $eventPath, $frame->{FrameId} ); $frame->{capturePath} = sprintf(
rename( $frame->{imagePath}, $frame->{capturePath} ) or Fatal( "Can't copy ".$frame->{imagePath}." to ".$frame->{capturePath}.": $!" ); "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}
."d-capture.jpg"
, $eventPath
, $frame->{FrameId}
);
rename( $frame->{imagePath}, $frame->{capturePath} )
or Fatal( "Can't copy ".$frame->{imagePath}
." to ".$frame->{capturePath}.": $!"
);
setFileOwner( $frame->{capturePath} ); setFileOwner( $frame->{capturePath} );
if ( 0 && $Config{ZM_CREATE_ANALYSIS_IMAGES} ) if ( 0 && $Config{ZM_CREATE_ANALYSIS_IMAGES} )
{ {
$frame->{analysePath} = sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-analyse.jpg", $eventPath, $frame->{FrameId} ); $frame->{analysePath} = sprintf(
link( $frame->{capturePath}, $frame->{analysePath} ) or Fatal( "Can't link ".$frame->{capturePath}." to ".$frame->{analysePath}.": $!" ); "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}
."d-analyse.jpg"
, $eventPath
, $frame->{FrameId}
);
link( $frame->{capturePath}, $frame->{analysePath} )
or Fatal( "Can't link ".$frame->{capturePath}
." to ".$frame->{analysePath}.": $!"
);
setFileOwner( $frame->{analysePath} ); setFileOwner( $frame->{analysePath} );
} }
} }
@ -469,7 +527,8 @@ sub updateEvent
my $dbh = zmDbConnect(); my $dbh = zmDbConnect();
$event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id}
if ( $event->{Name} eq 'New Event' );
my %formats = ( my %formats = (
StartTime => 'from_unixtime(?)', StartTime => 'from_unixtime(?)',
@ -486,8 +545,10 @@ sub updateEvent
my $sql = "update Events set ".join(',',@sets)." where Id = ?"; my $sql = "update Events set ".join(',',@sets)." where Id = ?";
push( @values, $event->{Id} ); push( @values, $event->{Id} );
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql )
my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
my $res = $sth->execute( @values )
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
} }
sub deleteEventFiles sub deleteEventFiles

View File

@ -258,7 +258,12 @@ sub initialise( @ )
{ {
foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) )
{ {
if ( $target eq $this->{id} || $target eq "_".$this->{id} || $target eq $this->{idRoot} || $target eq "_".$this->{idRoot} || $target eq "" ) if ( $target eq $this->{id}
|| $target eq "_".$this->{id}
|| $target eq $this->{idRoot}
|| $target eq "_".$this->{idRoot}
|| $target eq ""
)
{ {
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG )
{ {
@ -288,7 +293,14 @@ sub initialise( @ )
$this->{initialised} = !undef; $this->{initialised} = !undef;
Debug( "LogOpts: level=".$codes{$this->{level}}."/".$codes{$this->{effectiveLevel}}.", screen=".$codes{$this->{termLevel}}.", database=".$codes{$this->{databaseLevel}}.", logfile=".$codes{$this->{fileLevel}}."->".$this->{logFile}.", syslog=".$codes{$this->{syslogLevel}} ); Debug( "LogOpts: level=".$codes{$this->{level}}
."/".$codes{$this->{effectiveLevel}}
.", screen=".$codes{$this->{termLevel}}
.", database=".$codes{$this->{databaseLevel}}
.", logfile=".$codes{$this->{fileLevel}}
."->".$this->{logFile}
.", syslog=".$codes{$this->{syslogLevel}}
);
} }
sub terminate sub terminate
@ -452,23 +464,39 @@ sub databaseLevel
if ( defined($port) ) if ( defined($port) )
{ {
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$host.";port=".$port, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$host
.";port=".$port
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);
} }
else else
{ {
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);
} }
if ( !$this->{dbh} ) if ( !$this->{dbh} )
{ {
$databaseLevel = NOLOG; $databaseLevel = NOLOG;
Error( "Unable to write log entries to DB, can't connect to database '".$Config{ZM_DB_NAME}."' on host '".$Config{ZM_DB_HOST}."'" ); Error( "Unable to write log entries to DB, can't connect to database '"
.$Config{ZM_DB_NAME}
."' on host '"
.$Config{ZM_DB_HOST}
."'"
);
} }
else else
{ {
$this->{dbh}->{AutoCommit} = 1; $this->{dbh}->{AutoCommit} = 1;
Fatal( "Can't set AutoCommit on in database connection" ) unless( $this->{dbh}->{AutoCommit} ); Fatal( "Can't set AutoCommit on in database connection" )
unless( $this->{dbh}->{AutoCommit} );
$this->{dbh}->{mysql_auto_reconnect} = 1; $this->{dbh}->{mysql_auto_reconnect} = 1;
Fatal( "Can't set mysql_auto_reconnect on in database connection" ) unless( $this->{dbh}->{mysql_auto_reconnect} ); Fatal( "Can't set mysql_auto_reconnect on in database connection" )
unless( $this->{dbh}->{mysql_auto_reconnect} );
$this->{dbh}->trace( 0 ); $this->{dbh}->trace( 0 );
} }
} }
@ -559,7 +587,9 @@ sub openFile
if ( $> == 0 ) if ( $> == 0 )
{ {
chown( $webUid, $webGid, $this->{logFile} ) chown( $webUid, $webGid, $this->{logFile} )
or Fatal( "Can't change permissions on log file '".$this->{logFile}."': $!" ) or Fatal( "Can't change permissions on log file '"
.$this->{logFile}."': $!"
)
} }
} }
else else
@ -588,7 +618,17 @@ sub logPrint
my $code = $codes{$level}; my $code = $codes{$level};
my ($seconds, $microseconds) = gettimeofday(); my ($seconds, $microseconds) = gettimeofday();
my $message = sprintf( "%s.%06d %s[%d].%s [%s]", strftime( "%x %H:%M:%S", localtime( $seconds ) ), $microseconds, $this->{id}, $$, $code, $string ); my $message = sprintf(
"%s.%06d %s[%d].%s [%s]"
, strftime( "%x %H:%M:%S"
,localtime( $seconds )
)
, $microseconds
, $this->{id}
, $$
, $code
, $string
);
if ( $this->{trace} ) if ( $this->{trace} )
{ {
$message = Carp::shortmess( $message ); $message = Carp::shortmess( $message );
@ -597,7 +637,8 @@ sub logPrint
{ {
$message = $message."\n"; $message = $message."\n";
} }
syslog( $priorities{$level}, $code." [%s]", $string ) if ( $level <= $this->{syslogLevel} ); syslog( $priorities{$level}, $code." [%s]", $string )
if ( $level <= $this->{syslogLevel} );
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} ); print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} );
if ( $level <= $this->{databaseLevel} ) if ( $level <= $this->{databaseLevel} )
{ {
@ -608,7 +649,14 @@ sub logPrint
$this->{databaseLevel} = NOLOG; $this->{databaseLevel} = NOLOG;
Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->errstr() ); Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->errstr() );
} }
my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0), $this->{id}, $$, $level, $code, $string, $this->{fileName} ); my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0)
, $this->{id}
, $$
, $level
, $code
, $string
, $this->{fileName}
);
if ( !$res ) if ( !$res )
{ {
$this->{databaseLevel} = NOLOG; $this->{databaseLevel} = NOLOG;
@ -758,11 +806,19 @@ ZoneMinder::Logger - ZoneMinder Logger module
=head1 DESCRIPTION =head1 DESCRIPTION
The ZoneMinder:Logger module contains the common debug and error reporting routines used by the ZoneMinder scripts. The ZoneMinder:Logger module contains the common debug and error reporting
routines used by the ZoneMinder scripts.
To use debug in your scripts you need to include this module, and call logInit. Thereafter you can sprinkle Debug or Error calls etc throughout the code safe in the knowledge that they will be reported to your error log, and possibly the syslogger, in a meaningful and consistent format. To use debug in your scripts you need to include this module, and call
logInit. Thereafter you can sprinkle Debug or Error calls etc throughout
the code safe in the knowledge that they will be reported to your error
log, and possibly the syslogger, in a meaningful and consistent format.
Debug is discussed in terms of levels where 1 and above (currently only 1 for scripts) is considered debug, 0 is considered as informational, -1 is a warning, -2 is an error and -3 is a fatal error or panic. Where levels are mentioned below as thresholds the value given and anything with a lower level (ie. more serious) will be included. Debug is discussed in terms of levels where 1 and above (currently only 1
for scripts) is considered debug, 0 is considered as informational, -1 is a
warning, -2 is an error and -3 is a fatal error or panic. Where levels are
mentioned below as thresholds the value given and anything with a lower
level (ie. more serious) will be included.
=head1 METHODS =head1 METHODS
@ -770,7 +826,12 @@ Debug is discussed in terms of levels where 1 and above (currently only 1 for sc
=item logInit ( $id, %options ); =item logInit ( $id, %options );
Initialises the debug and prepares the logging for forthcoming operations. If not called explicitly it will be called by the first debug call in your script, but with default (and probably meaningless) options. The only compulsory arguments are $id which must be a string that will identify debug coming from this script in mixed logs. Other options may be provided as below, Initialises the debug and prepares the logging for forthcoming operations.
If not called explicitly it will be called by the first debug call in your
script, but with default (and probably meaningless) options. The only
compulsory arguments are $id which must be a string that will identify
debug coming from this script in mixed logs. Other options may be provided
as below,
Option Default Description Option Default Description
--------- --------- ----------- --------- --------- -----------
@ -807,27 +868,39 @@ These methods can be used to get and set the current settings as defined in logI
=item Debug( $string ); =item Debug( $string );
This method will output a debug message if the current debug level permits it, otherwise does nothing. This message will be tagged with the DBG string in the logs. This method will output a debug message if the current debug level permits
it, otherwise does nothing. This message will be tagged with the DBG string
in the logs.
=item Info( $string ); =item Info( $string );
This method will output an informational message if the current debug level permits it, otherwise does nothing. This message will be tagged with the INF string in the logs. This method will output an informational message if the current debug level
permits it, otherwise does nothing. This message will be tagged with the
INF string in the logs.
=item Warning( $string ); =item Warning( $string );
This method will output a warning message if the current debug level permits it, otherwise does nothing. This message will be tagged with the WAR string in the logs. This method will output a warning message if the current debug level
permits it, otherwise does nothing. This message will be tagged with the
WAR string in the logs.
=item Error( $string ); =item Error( $string );
This method will output an error message if the current debug level permits it, otherwise does nothing. This message will be tagged with the ERR string in the logs. This method will output an error message if the current debug level permits
it, otherwise does nothing. This message will be tagged with the ERR string
in the logs.
=item Fatal( $string ); =item Fatal( $string );
This method will output a fatal error message and then die if the current debug level permits it, otherwise does nothing. This message will be tagged with the FAT string in the logs. This method will output a fatal error message and then die if the current
debug level permits it, otherwise does nothing. This message will be tagged
with the FAT string in the logs.
=item Panic( $string ); =item Panic( $string );
This method will output a panic error message and then die with a stack trace if the current debug level permits it, otherwise does nothing. This message will be tagged with the PNC string in the logs. This method will output a panic error message and then die with a stack
trace if the current debug level permits it, otherwise does nothing. This
message will be tagged with the PNC string in the logs.
=back =back
@ -845,7 +918,8 @@ The :all tag will export all above symbols.
Carp Carp
Sys::Syslog Sys::Syslog
The ZoneMinder README file Troubleshooting section for an extended discussion on the use and configuration of syslog with ZoneMinder. The ZoneMinder README file Troubleshooting section for an extended
discussion on the use and configuration of syslog with ZoneMinder.
http://www.zoneminder.com http://www.zoneminder.com

View File

@ -204,23 +204,37 @@ sub zmMemInit
} }
foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) ) foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) )
{ {
if ( $member_data->{type} eq "long" || $member_data->{type} eq "ulong" || $member_data->{type} eq "size_t") if ( $member_data->{type} eq "long"
|| $member_data->{type} eq "ulong"
|| $member_data->{type} eq "size_t"
)
{ {
$member_data->{size} = $member_data->{align} = $native; $member_data->{size} = $member_data->{align} = $native;
} }
elsif( $member_data->{type} eq "int64" || $member_data->{type} eq "uint64" || $member_data->{type} eq "time_t64") elsif( $member_data->{type} eq "int64"
|| $member_data->{type} eq "uint64"
|| $member_data->{type} eq "time_t64"
)
{ {
$member_data->{size} = $member_data->{align} = 8; $member_data->{size} = $member_data->{align} = 8;
} }
elsif ( $member_data->{type} eq "int32" || $member_data->{type} eq "uint32" || $member_data->{type} eq "bool4" ) elsif ( $member_data->{type} eq "int32"
|| $member_data->{type} eq "uint32"
|| $member_data->{type} eq "bool4"
)
{ {
$member_data->{size} = $member_data->{align} = 4; $member_data->{size} = $member_data->{align} = 4;
} }
elsif ($member_data->{type} eq "int16" || $member_data->{type} eq "uint16") elsif ($member_data->{type} eq "int16"
|| $member_data->{type} eq "uint16"
)
{ {
$member_data->{size} = $member_data->{align} = 2; $member_data->{size} = $member_data->{align} = 2;
} }
elsif ( $member_data->{type} eq "int8" || $member_data->{type} eq "uint8" || $member_data->{type} eq "bool1" ) elsif ( $member_data->{type} eq "int8"
|| $member_data->{type} eq "uint8"
|| $member_data->{type} eq "bool1"
)
{ {
$member_data->{size} = $member_data->{align} = 1; $member_data->{size} = $member_data->{align} = 1;
} }
@ -231,7 +245,9 @@ sub zmMemInit
} }
else else
{ {
Fatal( "Unexpected type '".$member_data->{type}."' found in shared data definition." ); Fatal( "Unexpected type '".$member_data->{type}
."' found in shared data definition."
);
} }
if ( $member_data->{align} > 1 && ($offset%$member_data->{align}) > 0 ) if ( $member_data->{align} > 1 && ($offset%$member_data->{align}) > 0 )
@ -265,11 +281,22 @@ sub zmMemVerify
{ {
if ( $sd_size ) if ( $sd_size )
{ {
Error( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); Error( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got "
.$sd_size
);
} }
else else
{ {
Debug( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); Debug( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got ".$sd_size
);
} }
return( undef ); return( undef );
} }
@ -278,11 +305,23 @@ sub zmMemVerify
{ {
if ( $td_size ) if ( $td_size )
{ {
Error( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); Error( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
} }
else else
{ {
Debug( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); Debug( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
} }
return( undef ); return( undef );
} }
@ -417,7 +456,8 @@ sub zmMemWrite
while ( my ( $field, $value ) = each( %$field_values ) ) while ( my ( $field, $value ) = each( %$field_values ) )
{ {
my ( $section, $element ) = split( /[\/:.]/, $field ); my ( $section, $element ) = split( /[\/:.]/, $field );
Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); Fatal( "Invalid shared data selector '$field'" )
if ( !$section || !$element );
my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; my $offset = $mem_data->{$section}->{contents}->{$element}->{offset};
my $type = $mem_data->{$section}->{contents}->{$element}->{type}; my $type = $mem_data->{$section}->{contents}->{$element}->{type};
@ -481,7 +521,9 @@ sub zmMemWrite
if ( !zmMemPut( $monitor, $offset, $size, $data ) ) if ( !zmMemPut( $monitor, $offset, $size, $data ) )
{ {
Error( "Unable to write '$value' to '$field' in memory for monitor ".$monitor->{Id} ); Error( "Unable to write '$value' to '$field' in memory for monitor "
.$monitor->{Id}
);
zmMemInvalidate( $monitor ); zmMemInvalidate( $monitor );
return( undef ); return( undef );
} }
@ -556,7 +598,10 @@ sub zmHasAlarmed
my $monitor = shift; my $monitor = shift;
my $last_event_id = shift; my $last_event_id = shift;
my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state"
,"shared_data:last_event"
]
);
if ( $state == STATE_ALARM || $state == STATE_ALERT ) if ( $state == STATE_ALARM || $state == STATE_ALERT )
{ {
@ -722,16 +767,29 @@ ZoneMinder::MappedMem - ZoneMinder Mapped Memory access module
} }
} }
( $lri, $lwi ) = zmMemRead( $monitor, [ "shared_data:last_read_index", "shared_data:last_write_index" ] ); ( $lri, $lwi ) = zmMemRead( $monitor, [ "shared_data:last_read_index",
"shared_data:last_write_index"
]
);
zmMemWrite( $monitor, { "trigger_data:trigger_showtext" => "Some Text" } ); zmMemWrite( $monitor, { "trigger_data:trigger_showtext" => "Some Text" } );
=head1 DESCRIPTION =head1 DESCRIPTION
The ZoneMinder:MappedMem module contains methods for accessing and writing to mapped memory as well as helper methods for common operations. The ZoneMinder:MappedMem module contains methods for accessing and writing
to mapped memory as well as helper methods for common operations.
The core elements of ZoneMinder used mapped memory to allow multiple access to resources. Although ZoneMinder scripts have used this information before, up until now it was difficult to access and prone to errors. This module introduces a common API for mapped memory access (both reading and writing) making it a lot easier to customise scripts or even create your own. The core elements of ZoneMinder used mapped memory to allow multiple access
to resources. Although ZoneMinder scripts have used this information
before, up until now it was difficult to access and prone to errors. This
module introduces a common API for mapped memory access (both reading and
writing) making it a lot easier to customise scripts or even create your
own.
All the methods listed below require a 'monitor' parameter. This must be a reference to a hash with at least the 'Id' field set to the monitor id of the mapped memory you wish to access. Using database methods to select the monitor details will also return this kind of data. Some of the mapped memory methods will add and amend new fields to this hash. All the methods listed below require a 'monitor' parameter. This must be a
reference to a hash with at least the 'Id' field set to the monitor id of
the mapped memory you wish to access. Using database methods to select the
monitor details will also return this kind of data. Some of the mapped
memory methods will add and amend new fields to this hash.
=head1 METHODS =head1 METHODS
@ -739,27 +797,46 @@ All the methods listed below require a 'monitor' parameter. This must be a refer
=item zmMemVerify ( $monitor ); =item zmMemVerify ( $monitor );
Verify that the mapped memory of the monitor given exists and is valid. It will return an undefined value if it is not valid. You should generally call this method first before using any of the other methods, but most of the remaining methods will also do so if the memory has not already been verified. Verify that the mapped memory of the monitor given exists and is valid. It
will return an undefined value if it is not valid. You should generally
call this method first before using any of the other methods, but most of
the remaining methods will also do so if the memory has not already been
verified.
=item zmMemInvalidate ( $monitor ); =item zmMemInvalidate ( $monitor );
Following an error, reset the mapped memory ids and attempt to reverify on the next operation. This is mostly used when a mapped memory segment has gone away and been recreated with a different id. Following an error, reset the mapped memory ids and attempt to reverify on
the next operation. This is mostly used when a mapped memory segment has
gone away and been recreated with a different id.
=item zmMemRead ( $monitor, $readspec ); =item zmMemRead ( $monitor, $readspec );
This method is used to read data from mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'readspec' must either be a string of the form "<section>:<field>" or a reference to an array of strings of the same format. In the first case a single value is returned, in the latter case a list of values is return. Errors will cause undefined to be returned. The allowable sections and field names are described below. This method is used to read data from mapped memory attached to the given
monitor. The mapped memory will be verified if it has not already been. The
'readspec' must either be a string of the form "<section>:<field>" or a
reference to an array of strings of the same format. In the first case a
single value is returned, in the latter case a list of values is return.
Errors will cause undefined to be returned. The allowable sections and
field names are described below.
=item zmMemWrite ( $monitor, $writespec ); =item zmMemWrite ( $monitor, $writespec );
This method is used to write data to mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'writespec' must be a reference to a hash with keys of the form "<section>:<field>" and values as the data to be written. Errors will cause undefined to be returned, otherwise a non-undefined value will be returned. The allowable sections and field names are described below. This method is used to write data to mapped memory attached to the given
monitor. The mapped memory will be verified if it has not already been. The
'writespec' must be a reference to a hash with keys of the form
"<section>:<field>" and values as the data to be written. Errors will cause
undefined to be returned, otherwise a non-undefined value will be returned.
The allowable sections and field names are described below.
=item $state = zmGetMonitorState ( $monitor ); =item $state = zmGetMonitorState ( $monitor );
Return the current state of the given monitor. This is an integer value and can be compared with the STATE constants given below. Return the current state of the given monitor. This is an integer value and
can be compared with the STATE constants given below.
=item $event_id = zmGetLastEvent ( $monitor ); =item $event_id = zmGetLastEvent ( $monitor );
Return the event id of the last event that the monitor generated, or 0 if no event has been generated by the current monitor process. Return the event id of the last event that the monitor generated, or 0 if
no event has been generated by the current monitor process.
=item zmIsAlarmed ( $monitor ); =item zmIsAlarmed ( $monitor );
@ -767,27 +844,38 @@ Return 1 if the monitor given is currently in an alarm state, 0 otherwise.
=item zmInAlarm ( $monitor ); =item zmInAlarm ( $monitor );
Return 1 if the monitor given is currently in an alarm or alerted state, 0 otherwise. Return 1 if the monitor given is currently in an alarm or alerted state, 0
otherwise.
=item zmHasAlarmed ( $monitor ); =item zmHasAlarmed ( $monitor );
Return 1 if the given monitor is in an alarm state, or has been in an alarm state since the last call to this method. Return 1 if the given monitor is in an alarm state, or has been in an alarm
state since the last call to this method.
=item ( $x, $y ) = zmGetAlarmLocation ( $monitor ); =item ( $x, $y ) = zmGetAlarmLocation ( $monitor );
Return an x,y pair indicating the image co-ordinates of the centre of the last motion event generated by the given monitor. If no event has been generated by the current monitor process, or the alarm was not motion related, returns -1,-1. Return an x,y pair indicating the image co-ordinates of the centre of the
last motion event generated by the given monitor. If no event has been
generated by the current monitor process, or the alarm was not motion
related, returns -1,-1.
=item zmGetLastWriteTime ( $monitor ); =item zmGetLastWriteTime ( $monitor );
Returns the time (in utc seconds) since the last image was captured by the given monitor and written to shared memory, or 0 otherwise. Returns the time (in utc seconds) since the last image was captured by the
given monitor and written to shared memory, or 0 otherwise.
=item zmGetLastReadTime ( $monitor ); =item zmGetLastReadTime ( $monitor );
Returns the time (in utc seconds) since the last image was read from shared memory by the analysis daemon of the given monitor, or 0 otherwise or if the monitor is in monitor only mode. Returns the time (in utc seconds) since the last image was read from shared
memory by the analysis daemon of the given monitor, or 0 otherwise or if
the monitor is in monitor only mode.
=item zmMonitorSuspend ( $monitor ); =item zmMonitorSuspend ( $monitor );
Suspend the given monitor from generating events caused by motion. This method can be used to prevent camera actions such as panning or zooming from causing events. If configured to do so, the monitor may automatically resume after a defined period. Suspend the given monitor from generating events caused by motion. This
method can be used to prevent camera actions such as panning or zooming
from causing events. If configured to do so, the monitor may automatically
resume after a defined period.
=item zmMonitorResume ( $monitor ); =item zmMonitorResume ( $monitor );
@ -795,25 +883,43 @@ Allow the given monitor to resume generating events caused by motion.
=item zmTriggerEventOn ( $monitor, $score, $cause [, $text, $showtext ] ); =item zmTriggerEventOn ( $monitor, $score, $cause [, $text, $showtext ] );
Trigger the given monitor to generate an event. You must supply an event score and a cause string indicating the reason for the event. You may also supply a text string containing further details about the event and a showtext string which may be included in the timestamp annotation on any images captured during the event, if configured to do so. Trigger the given monitor to generate an event. You must supply an event
score and a cause string indicating the reason for the event. You may also
supply a text string containing further details about the event and a
showtext string which may be included in the timestamp annotation on any
images captured during the event, if configured to do so.
=item zmTriggerEventOff ( $monitor ); =item zmTriggerEventOff ( $monitor );
Trigger the given monitor to not generate any events. This method does not cancel zmTriggerEventOn, but is exclusive to it. This method is intended to allow external triggers to prevent normal events being generated by monitors in the same way as zmMonitorSuspend but applies to all events and not just motion, and is intended for longer timescales than are appropriate for suspension. Trigger the given monitor to not generate any events. This method does not
cancel zmTriggerEventOn, but is exclusive to it. This method is intended to
allow external triggers to prevent normal events being generated by
monitors in the same way as zmMonitorSuspend but applies to all events and
not just motion, and is intended for longer timescales than are appropriate
for suspension.
=item zmTriggerEventCancel ( $monitor ); =item zmTriggerEventCancel ( $monitor );
Cancel any previous trigger on or off requests. This stops a triggered alarm if it exists from a previous 'on' and allows events to be generated once more following a previous 'off'. Cancel any previous trigger on or off requests. This stops a triggered
alarm if it exists from a previous 'on' and allows events to be generated
once more following a previous 'off'.
=item zmTriggerShowtext ( $monitor, $showtest ); =item zmTriggerShowtext ( $monitor, $showtest );
Indicate that the given text should be displayed in the timestamp annotation on any images captured, if the format of the annotation string defined for the monitor permits. Indicate that the given text should be displayed in the timestamp
annotation on any images captured, if the format of the annotation string
defined for the monitor permits.
=back =back
=head1 DATA =head1 DATA
The data fields in mapped memory that may be accessed are as follows. There are two main sections, shared_data which is general data and trigger_data which is used for event triggering. Whilst reading from these fields is harmless, extreme care must be taken when writing to mapped memory, especially in the shared_data section as this is normally written to only by monitor capture and analysis processes. The data fields in mapped memory that may be accessed are as follows. There
are two main sections, shared_data which is general data and trigger_data
which is used for event triggering. Whilst reading from these fields is
harmless, extreme care must be taken when writing to mapped memory,
especially in the shared_data section as this is normally written to only
by monitor capture and analysis processes.
shared_data The general mapped memory section shared_data The general mapped memory section
size The size, in bytes, of this section size The size, in bytes, of this section
@ -844,21 +950,38 @@ The data fields in mapped memory that may be accessed are as follows. There are
=head1 CONSTANTS =head1 CONSTANTS
The following constants are used by the methods above, but can also be used by user scripts if required. The following constants are used by the methods above, but can also be used
by user scripts if required.
=over 4 =over 4
=item STATE_IDLE STATE_PREALARM STATE_ALARM STATE_ALERT STATE_TAPE =item STATE_IDLE STATE_PREALARM STATE_ALARM STATE_ALERT STATE_TAPE
These constants define the state of the monitor with respect to alarms and events. They are used in the shared_data:state field. These constants define the state of the monitor with respect to alarms and
events. They are used in the shared_data:state field.
=item ACTION_GET ACTION_SET ACTION_RELOAD ACTION_SUSPEND ACTION_RESUME =item ACTION_GET ACTION_SET ACTION_RELOAD ACTION_SUSPEND ACTION_RESUME
These constants defines the various values that can exist in the shared_data:action field. This is a bitmask which when non-zero defines an action that an executing monitor process should take. ACTION_GET requires that the current values of brightness, contrast, colour and hue are taken from the camera and written to the equivalent mapped memory fields. ACTION_SET implies the reverse, that the values in mapped memory should be written to the camera. ACTION_RELOAD signal that the monitor process should reload itself from the database in case any settings have changed there. ACTION_SUSPEND signals that a monitor should stop exaiming images for motion, though other alarms may still occur. ACTION_RESUME sigansl that a monitor should resume motion detectiom. These constants defines the various values that can exist in the
shared_data:action field. This is a bitmask which when non-zero defines an
action that an executing monitor process should take. ACTION_GET requires
that the current values of brightness, contrast, colour and hue are taken
from the camera and written to the equivalent mapped memory fields.
ACTION_SET implies the reverse, that the values in mapped memory should be
written to the camera. ACTION_RELOAD signal that the monitor process should
reload itself from the database in case any settings have changed there.
ACTION_SUSPEND signals that a monitor should stop exaiming images for
motion, though other alarms may still occur. ACTION_RESUME sigansl that a
monitor should resume motion detectiom.
=item TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF =item TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF
These constants are used in the definition of external triggers. TRIGGER_CANCEL is used to indicated that any previous trigger settings should be cancelled, TRIGGER_ON signals that an alarm should be created (or continued)) as a result of the current trigger and TRIGGER_OFF signals that the trigger should prevent any alarms from being generated. See the trigger methods above for further details. These constants are used in the definition of external triggers.
TRIGGER_CANCEL is used to indicated that any previous trigger settings
should be cancelled, TRIGGER_ON signals that an alarm should be created (or
continued)) as a result of the current trigger and TRIGGER_OFF signals that
the trigger should prevent any alarms from being generated. See the trigger
methods above for further details.
=back =back

View File

@ -86,14 +86,22 @@ sub zmMemAttach
my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id}; my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id};
if ( ! -e $mmap_file ) { if ( ! -e $mmap_file ) {
Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running.", $mmap_file ) ); Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running."
, $mmap_file
)
);
return ( undef ); return ( undef );
} }
my $mmap_file_size = -s $mmap_file; my $mmap_file_size = -s $mmap_file;
if ( $mmap_file_size < $size ) { if ( $mmap_file_size < $size ) {
Error( sprintf( "Memory map file '%s' should have been %d but was instead %d", $mmap_file, $size, $mmap_file_size ) ); Error( sprintf( "Memory map file '%s' should have been %d but was instead %d"
, $mmap_file
, $size
, $mmap_file_size
)
);
return ( undef ); return ( undef );
} }
if ( !open( MMAP, "+<", $mmap_file ) ) if ( !open( MMAP, "+<", $mmap_file ) )
@ -147,7 +155,10 @@ sub zmMemGet
my $mmap = $monitor->{MMap}; my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap ) if ( !$mmap || !$$mmap )
{ {
Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
);
return( undef ); return( undef );
} }
my $data = substr( $$mmap, $offset, $size ); my $data = substr( $$mmap, $offset, $size );
@ -164,7 +175,10 @@ sub zmMemPut
my $mmap = $monitor->{MMap}; my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap ) if ( !$mmap || !$$mmap )
{ {
Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
);
return( undef ); return( undef );
} }
substr( $$mmap, $offset, $size ) = $data; substr( $$mmap, $offset, $size ) = $data;

View File

@ -85,7 +85,11 @@ sub zmMemAttach
my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 ); my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 );
if ( !defined($shm_id) ) if ( !defined($shm_id) )
{ {
Error( sprintf( "Can't get shared memory id '%x', %d: $!\n", $shm_key, $monitor->{Id} ) ); Error( sprintf( "Can't get shared memory id '%x', %d: $!\n"
, $shm_key
, $monitor->{Id}
)
);
return( undef ); return( undef );
} }
$monitor->{ShmKey} = $shm_key; $monitor->{ShmKey} = $shm_key;
@ -113,7 +117,11 @@ sub zmMemGet
my $data; my $data;
if ( !shmread( $shm_id, $data, $offset, $size ) ) if ( !shmread( $shm_id, $data, $offset, $size ) )
{ {
Error( sprintf( "Can't read from shared memory '%x/%d': $!", $shm_key, $shm_id ) ); Error( sprintf( "Can't read from shared memory '%x/%d': $!"
, $shm_key
, $shm_id
)
);
return( undef ); return( undef );
} }
return( $data ); return( $data );
@ -131,7 +139,11 @@ sub zmMemPut
if ( !shmwrite( $shm_id, $data, $offset, $size ) ) if ( !shmwrite( $shm_id, $data, $offset, $size ) )
{ {
Error( sprintf( "Can't write to shared memory '%x/%d': $!", $shm_key, $shm_id ) ); Error( sprintf( "Can't write to shared memory '%x/%d': $!"
, $shm_key
, $shm_id
)
);
return( undef ); return( undef );
} }
return( !undef ); return( !undef );
@ -141,7 +153,10 @@ sub zmMemClean
{ {
Debug( "Removing shared memory\n" ); Debug( "Removing shared memory\n" );
# Find ZoneMinder shared memory # Find ZoneMinder shared memory
my $command = "ipcs -m | grep '^".substr( sprintf( "0x%x", hex($Config{ZM_SHM_KEY}) ), 0, -2 )."'"; my $command = "ipcs -m | grep '^"
.substr( sprintf( "0x%x", hex($Config{ZM_SHM_KEY}) ), 0, -2 )
."'"
;
Debug( "Checking for shared memory with '$command'\n" ); Debug( "Checking for shared memory with '$command'\n" );
open( my $CMD, '<', "$command |" ) open( my $CMD, '<', "$command |" )
or Fatal( "Can't execute '$command': $!" ); or Fatal( "Can't execute '$command': $!" );

View File

@ -87,7 +87,13 @@ sub write
my $nbytes = syswrite( $self->{handle}, $buffer ); my $nbytes = syswrite( $self->{handle}, $buffer );
if ( !defined( $nbytes) || $nbytes < length($buffer) ) if ( !defined( $nbytes) || $nbytes < length($buffer) )
{ {
Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".($nbytes?$nbytes:'undefined').": $!\n" ); Error( "Unable to write buffer '".$buffer
.", expected "
.length($buffer)
." bytes, sent "
.($nbytes?$nbytes:'undefined')
.": $!\n"
);
return( undef ); return( undef );
} }
Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); Debug( "Wrote '$buffer' ($nbytes bytes)\n" );

View File

@ -62,7 +62,8 @@ sub open
my $self = shift; my $self = shift;
local *sfh; local *sfh;
my $saddr = sockaddr_in( $self->{port}, INADDR_ANY ); my $saddr = sockaddr_in( $self->{port}, INADDR_ANY );
socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or croak( "Can't open socket: $!" ); socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') )
or croak( "Can't open socket: $!" );
setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 ); setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 );
bind( *sfh, $saddr ) or croak( "Can't bind: $!" ); bind( *sfh, $saddr ) or croak( "Can't bind: $!" );
listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" ); listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" );

View File

@ -99,7 +99,13 @@ sub write
$self->{device}->write_drain(); $self->{device}->write_drain();
if ( !defined( $nbytes) || $nbytes < length($buffer) ) if ( !defined( $nbytes) || $nbytes < length($buffer) )
{ {
Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".$nbytes.": $!\n" ); Error( "Unable to write buffer '".$buffer
.", expected "
.length($buffer)
." bytes, sent "
.$nbytes
.": $!\n"
);
return( undef ); return( undef );
} }
Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); Debug( "Wrote '$buffer' ($nbytes bytes)\n" );

View File

@ -151,7 +151,13 @@ sub putMessages
$buffer .= "\n"; $buffer .= "\n";
if ( !$self->{channel}->write( $buffer ) ) if ( !$self->{channel}->write( $buffer ) )
{ {
Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); Error( "Unable to write buffer '".$buffer
." to connection "
.$self->{name}
." ("
.$self->fileno()
.")\n"
);
} }
} }
return( undef ); return( undef );

View File

@ -1554,8 +1554,10 @@ void EventStream::runStream()
if ( ((curr_frame_id-1)%frame_mod) == 0 ) if ( ((curr_frame_id-1)%frame_mod) == 0 )
{ {
delta_us = (unsigned int)(frame_data->delta * 1000000); delta_us = (unsigned int)(frame_data->delta * 1000000);
if ( effective_fps < base_fps ) // if effective > base we should speed up frame delivery
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
// but must not exceed maxfps
delta_us = max(delta_us, 1000000 / maxfps);
send_frame = true; send_frame = true;
} }
} }

View File

@ -427,7 +427,7 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
// Close current stream. // Close current stream.
camera->CloseFfmpeg(); camera->CloseFfmpeg();
// Sleep if neccessary to not reconnect too fast. // Sleep if necessary to not reconnect too fast.
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
wait = wait < 0 ? 0 : wait; wait = wait < 0 ? 0 : wait;
if (wait > 0){ if (wait > 0){

View File

@ -305,7 +305,7 @@ void Image::Initialise()
initialised = true; initialised = true;
} }
/* Requests a writeable buffer to the image. This is safer than buffer() because this way we can gurantee that a buffer of required size exists */ /* Requests a writeable buffer to the image. This is safer than buffer() because this way we can guarantee that a buffer of required size exists */
uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) { uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) {
unsigned int newsize; unsigned int newsize;
@ -1593,7 +1593,7 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres
return( result ); return( result );
} }
/* New function to allow buffer re-using instead of allocationg memory for the delta image everytime */ /* New function to allow buffer re-using instead of allocationg memory for the delta image every time */
void Image::Delta( const Image &image, Image* targetimage) const void Image::Delta( const Image &image, Image* targetimage) const
{ {
#ifdef ZM_IMAGE_PROFILING #ifdef ZM_IMAGE_PROFILING

View File

@ -32,6 +32,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <limits.h>
/* Workaround for GNU/kFreeBSD */
#if defined(__FreeBSD_kernel__)
#ifndef ENODATA
#define ENODATA ENOATTR
#endif
#endif
static unsigned int BigEndian; static unsigned int BigEndian;
static int vidioctl( int fd, int request, void *arg ) static int vidioctl( int fd, int request, void *arg )
@ -255,7 +262,7 @@ static PixelFormat getFfPixFormatFromV4lPalette( int v4l_version, int palette )
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
static char palette_desc[32]; static char palette_desc[32];
/* Automatic format selection prefered formats */ /* Automatic format selection preferred formats */
static const uint32_t prefered_rgb32_formats[] = {V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_rgb32_formats[] = {V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420};
static const uint32_t prefered_rgb24_formats[] = {V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_rgb24_formats[] = {V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420};
static const uint32_t prefered_gray8_formats[] = {V4L2_PIX_FMT_GREY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_gray8_formats[] = {V4L2_PIX_FMT_GREY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420};

View File

@ -535,7 +535,12 @@ void Logger::logPrint( bool hex, const char * const file, const int line, const
if (tid < 0 ) // Thread/Process id if (tid < 0 ) // Thread/Process id
#else #else
#ifdef HAVE_SYSCALL #ifdef HAVE_SYSCALL
#ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
# else
if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id
#endif
#endif // HAVE_SYSCALL #endif // HAVE_SYSCALL
#endif #endif
tid = getpid(); // Process id tid = getpid(); // Process id

View File

@ -470,7 +470,7 @@ Monitor::Monitor(
} }
} }
// Will this not happen everytime a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. // Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after..
if ( !n_zones ) { if ( !n_zones ) {
Debug( 1, "Monitor %s has no zones, adding one.", name ); Debug( 1, "Monitor %s has no zones, adding one.", name );
n_zones = 1; n_zones = 1;
@ -558,14 +558,18 @@ bool Monitor::connect() {
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
return false; return false;
} else { } else {
#ifdef MAP_LOCKED
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 );
if ( mem_ptr == MAP_FAILED ) { if ( mem_ptr == MAP_FAILED ) {
if ( errno == EAGAIN ) { if ( errno == EAGAIN ) {
Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size );
#endif
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size ); Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size );
#ifdef MAP_LOCKED
} }
} }
#endif
if ( mem_ptr == MAP_FAILED ) if ( mem_ptr == MAP_FAILED )
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
} }
@ -3150,7 +3154,7 @@ bool Monitor::closeEvent()
* comparing it with ZM_COLOUR_RGB24 or ZM_COLOUR_RGB32 is the way ), and then * comparing it with ZM_COLOUR_RGB24 or ZM_COLOUR_RGB32 is the way ), and then
* manage che check using RGB_VAL_RED() and so on macros instead of just RED(). * manage che check using RGB_VAL_RED() and so on macros instead of just RED().
* *
* Be carefull that in 32 bit images we need to check also where the alpha channel is, so, * Be careful that in 32 bit images we need to check also where the alpha channel is, so,
* (RGBA and BGRA) or (ABGR and ARGB) aren't the same! * (RGBA and BGRA) or (ABGR and ARGB) aren't the same!
* *
* To check black pixels in 32 bit images i can do a more efficient way using * To check black pixels in 32 bit images i can do a more efficient way using

View File

@ -262,7 +262,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
/* emit one intra frame every second */ /* emit one intra frame every second */
c->gop_size = frame_rate; c->gop_size = frame_rate;
// some formats want stream headers to be seperate // some formats want stream headers to be separate
if ( of->flags & AVFMT_GLOBALHEADER ) if ( of->flags & AVFMT_GLOBALHEADER )
c->flags |= CODEC_FLAG_GLOBAL_HEADER; c->flags |= CODEC_FLAG_GLOBAL_HEADER;
} }

View File

@ -392,7 +392,7 @@ int RtspThread::run()
if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) )
{ {
mUrl = trimSpaces( lines[i].substr( 13 ) ); mUrl = trimSpaces( lines[i].substr( 13 ) );
Info("Recieved new Content-Base in DESCRIBE reponse header. Updated device Url to: '%s'", mUrl.c_str() ); Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
break; break;
} }
} }

View File

@ -64,12 +64,19 @@ RETSIGTYPE zm_die_handler(int signal)
info->si_uid, info->si_status); info->si_uid, info->si_status);
ucontext_t *uc = (ucontext_t *) context; ucontext_t *uc = (ucontext_t *) context;
cr2 = info->si_addr;
#if defined(__x86_64__) #if defined(__x86_64__)
cr2 = info->si_addr; #ifdef __FreeBSD_kernel__
ip = (void *)(uc->uc_mcontext.mc_rip);
#else
ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]); ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]);
#endif
#else #else
cr2 = info->si_addr; #ifdef __FreeBSD_kernel__
ip = (void *)(uc->uc_mcontext.mc_eip);
#else
ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]); ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]);
#endif
#endif // defined(__x86_64__) #endif // defined(__x86_64__)
// Print the signal address and instruction pointer if available // Print the signal address and instruction pointer if available

View File

@ -36,15 +36,19 @@ class ThreadException : public Exception
{ {
private: private:
pid_t pid() { pid_t pid() {
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
tid=syscall(SYS_gettid); #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
# else
tid=syscall(SYS_gettid);
#endif
#endif #endif
return tid; return tid;
} }
public: public:
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) { ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
@ -219,13 +223,18 @@ protected:
pid_t id() const pid_t id() const
{ {
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
tid=syscall(SYS_gettid); #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
#else
tid=syscall(SYS_gettid);
#endif
#endif #endif
return tid; return tid;
} }

View File

@ -40,7 +40,11 @@ private:
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
#ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
#else
tid=syscall(SYS_gettid); tid=syscall(SYS_gettid);
#endif
#endif #endif
return tid; return tid;
} }

View File

@ -1,4 +1,6 @@
<?php <?php
# We use session vars in here, so we need to restart the session because we stopped it in index.php to improve concurrency.
session_start();
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) { if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
ajaxError( "No event id(s) supplied" ); ajaxError( "No event id(s) supplied" );

View File

@ -63,30 +63,30 @@ if ( isset($_GET['skin']) )
$skin = $_GET['skin']; $skin = $_GET['skin'];
elseif ( isset($_COOKIE['zmSkin']) ) elseif ( isset($_COOKIE['zmSkin']) )
$skin = $_COOKIE['zmSkin']; $skin = $_COOKIE['zmSkin'];
elseif ( defined(ZM_SKIN_DEFAULT) ) elseif ( defined('ZM_SKIN_DEFAULT') )
$skin = ZM_SKIN_DEFAULT; $skin = ZM_SKIN_DEFAULT;
else else
$skin = "classic"; $skin = "classic";
$skins = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) ); $skins = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) );
if ( ! in_array( $skin, $skins ) ) { if ( ! in_array( $skin, $skins ) ) {
Error( "Invalid skin '$skin'" ); Error( "Invalid skin '$skin' setting to " . $skins[0] );
$skin = 'classic'; $skin = $skins[0];
} }
if ( isset($_GET['css']) ) if ( isset($_GET['css']) )
$css = $_GET['css']; $css = $_GET['css'];
elseif ( isset($_COOKIE['zmCSS']) ) elseif ( isset($_COOKIE['zmCSS']) )
$css = $_COOKIE['zmCSS']; $css = $_COOKIE['zmCSS'];
elseif (defined(ZM_CSS_DEFAULT)) elseif (defined('ZM_CSS_DEFAULT'))
$css = ZM_CSS_DEFAULT; $css = ZM_CSS_DEFAULT;
else else
$css = "classic"; $css = "classic";
$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) ); $css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) );
if ( ! in_array( $css, $css_skins ) ) { if ( ! in_array( $css, $css_skins ) ) {
Error( "Invalid skin css '$css'" ); Error( "Invalid skin css '$css' setting to " . $css_skins[0] );
$css = 'classic'; $css = $css_skins[0];
} }
define( "ZM_BASE_PATH", dirname( $_SERVER['REQUEST_URI'] ) ); define( "ZM_BASE_PATH", dirname( $_SERVER['REQUEST_URI'] ) );
@ -102,13 +102,13 @@ ini_set( "session.name", "ZMSESSID" );
session_start(); session_start();
if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) ) if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin )
{ {
$_SESSION['skin'] = $skin; $_SESSION['skin'] = $skin;
setcookie( "zmSkin", $skin, time()+3600*24*30*12*10 ); setcookie( "zmSkin", $skin, time()+3600*24*30*12*10 );
} }
if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) ) { if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) {
$_SESSION['css'] = $css; $_SESSION['css'] = $css;
setcookie( "zmCSS", $css, time()+3600*24*30*12*10 ); setcookie( "zmCSS", $css, time()+3600*24*30*12*10 );
} }
@ -143,6 +143,10 @@ if ( ZM_OPT_USE_AUTH && ! isset($user) && $view != 'login' ) {
$view = 'login'; $view = 'login';
} }
# Only one request can open the session file at a time, so let's close the session here to improve concurrency.
# Any file/page that uses the session must re-open it.
session_write_close();
if ( isset( $_REQUEST['request'] ) ) if ( isset( $_REQUEST['request'] ) )
{ {
foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile )

View File

@ -24,6 +24,9 @@ if ( !canView( 'Events' ) )
return; return;
} }
# Must re-start session because we close it now in index.php to improve concurrency
session_start();
if ( isset($_SESSION['export']) ) if ( isset($_SESSION['export']) )
{ {
if ( isset($_SESSION['export']['detail']) ) if ( isset($_SESSION['export']['detail']) )