diff --git a/onvif/scripts/zmonvif-watch.pl b/onvif/scripts/zmonvif-watch.pl index 3af6a88c2..3b97b1805 100755 --- a/onvif/scripts/zmonvif-watch.pl +++ b/onvif/scripts/zmonvif-watch.pl @@ -24,6 +24,11 @@ # This module contains the implementation of the ONVIF event watcher # +## SETUP: +## chmod g+rw /dev/shm/zm* +## chgrp users /dev/shm/zm* +## systemctl stop iptables + use ZoneMinder; use DBI; @@ -40,6 +45,7 @@ use SOAP::Transport::HTTP; use Time::HiRes qw( usleep ); use Data::Dump qw(dump); +use Carp; # ======================================================================== # Constants @@ -53,24 +59,26 @@ use constant MONITOR_RELOAD_INTERVAL => 300; # Globals my $verbose = 0; -my $client; my $daemon_pid; my $dbh; my %monitors; my $monitor_reload_time = 0; +# this does not work on all architectures my @EXTRA_SOCK_OPTS = ( 'ReuseAddr' => '1', 'ReusePort' => '1' ); + # ========================================================================= # signal handling sub handler { # 1st argument is signal name my($sig) = @_; print "Caught a SIG$sig -- shutting down\n"; + confess(); if(defined $daemon_pid){ kill($daemon_pid); } @@ -132,7 +140,8 @@ sub loadMonitors my %new_monitors = (); - my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' ) and ConfigType='ONVIF'"; +# my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' )>0 and ConfigType='ONVIF'"; + my $sql = "select * from Monitors where ConfigType='ONVIF'"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) @@ -155,11 +164,43 @@ sub loadMonitors { $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } + + print "URL: " .$monitor->{ConfigURL}. "\n"; + + ## set up ONVIF client for monitor + next if( ! $monitor->{ConfigURL} ); + + my $soap_version; + if($monitor->{ConfigOptions} =~ /SOAP1([12])/) { + $soap_version = "1.$1"; + } + else { + $soap_version = "1.1"; + } + my $client = ONVIF::Client->new( { + 'url_svc_device' => $monitor->{ConfigURL}, + 'soap_version' => $soap_version } ); + + if($monitor->{User}) { + $client->set_credentials($monitor->{User}, $monitor->{Pass}, 0); + } + + $client->create_services(); + $monitor->{onvif_client} = $client; + $new_monitors{$monitor->{Id}} = $monitor; } %monitors = %new_monitors; } +sub freeMonitors +{ + foreach my $monitor ( values(%monitors) ) + { + # Free up any used memory handle + zmMemInvalidate( $monitor ); + } +} # ========================================================================= ### (experimental) send email @@ -271,15 +312,9 @@ require WSNotification::Elements::Subscribe; sub subscribe { - my ($localaddr, $topic_str, $duration) = @_; - -# my $topic = WSNotification::Types::TopicExpressionType->new( -# xmlattr => { -# Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet", -# }, -# value => $topic_str -# ); + my ($client, $localaddr, $topic_str, $duration) = @_; +# for debugging: # $client->get_endpoint('events')->no_dispatch(1); my $result = $client->get_endpoint('events')->Subscribe( { @@ -290,16 +325,16 @@ sub subscribe # Metadata => { # WSNotification::Types::MetadataType # }, }, -# Filter => { # WSNotification::Types::FilterType -# TopicExpression => { # WSNotification::Types::TopicExpressionType -# xmlattr => { -# Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet", -# }, -# value => "tns1:RuleEngine//.", -# }, + Filter => { # WSNotification::Types::FilterType + TopicExpression => { # WSNotification::Types::TopicExpressionType + xmlattr => { + Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet", + }, + value => $topic_str, + }, # MessageContent => { # WSNotification::Types::QueryExpressionType # }, -# }, + }, InitialTerminationTime => xs_duration($duration), # AbsoluteOrRelativeTimeType # SubscriptionPolicy => { # }, @@ -346,17 +381,13 @@ sub events { my ($localip, $localport) = @_; - Info( "Trigger daemon starting\n" ); + Info( "ONVIF Trigger daemon starting\n" ); if(!defined $localip) { $localip = '192.168.0.2'; $localport = '0'; } - - - my $event_svc = $client->get_endpoint('events'); - # re-use local address/port # @LWP::Protocol::http::EXTRA_SOCK_OPTS = *LWP::Protocol::http::_extra_sock_opts = sub @@ -378,39 +409,52 @@ sub events $port =~ s|/.*$||; $localaddr = $localip . ':' . $port; - -# ReuseAddr, ReusePort - -# print "Contact to SOAP server at ", $daemon->url, "\n"; - print "Server using local address " . $localaddr . "\n"; + print "Daemon uses local address " . $localaddr . "\n"; # This value is passed as the LocalAddr argument to IO::Socket::INET. my $transport = SOAP::Transport::HTTP::Client->new( - 'local_address' => $localaddr ); - -# dump( $transport ); - - $event_svc->set_transport($transport); - print "Sending from local address " . - $event_svc->get_transport()->local_address . "\n"; +# 'local_address' => $localaddr ); ## REUSE port + 'local_address' => $localip ); -#print xs_duration(89); -#exit(1); - - my $submgr_svc = subscribe($localaddr, 'tns1:RuleEngine//.', SUBSCRIPTION_RENEW_INTERVAL); - if(!$submgr_svc) { - exit; + loadMonitors(); + + foreach $monitor (values(%monitors)) { + + my $client = $monitor->{onvif_client}; + my $event_svc = $client->get_endpoint('events'); + $event_svc->set_transport($transport); +# print "Sending from local address " . +# $event_svc->get_transport()->local_address . "\n"; + + my $submgr_svc = subscribe($client, $localaddr, 'tns1:RuleEngine//.', SUBSCRIPTION_RENEW_INTERVAL); + + if(!$submgr_svc) { + print "Subscription failed\n"; + next; + } + + $monitor->{submgr_svc} = $submgr_svc; } - + while(1) { print "Sleeping for " . (SUBSCRIPTION_RENEW_INTERVAL - SUBSCRIPTION_RENEW_EARLY) . " seconds\n"; sleep(SUBSCRIPTION_RENEW_INTERVAL - SUBSCRIPTION_RENEW_EARLY); print "Renewal\n"; - renew($submgr_svc, SUBSCRIPTION_RENEW_INTERVAL + SUBSCRIPTION_RENEW_EARLY); + foreach $monitor (%monitors) { + if(defined $monitor->{submgr_svc}) { + renew($monitor->{submgr_svc}, SUBSCRIPTION_RENEW_INTERVAL + SUBSCRIPTION_RENEW_EARLY); + } + } }; - unsubscribe($submgr_svc); + Info( "ONVIF Trigger daemon exited\n" ); + + foreach $monitor (values(%monitors)) { + if(defined $monitor->{submgr_svc}) { + unsubscribe($monitor->{submgr_svc}); + } + } } # ========================================================================= @@ -462,39 +506,23 @@ sub metadata sub HELP_MESSAGE { my ($fh, $pkg, $ver, $opts) = @_; - print $fh "Usage: " . __FILE__ . " [-v] \n"; + print $fh "Usage: " . __FILE__ . " \n"; print $fh < \$action, - 'device|d=s' => \$url_svc_device, - 'soap-version|s=s' => \$soap_version, - 'username|u=s' => \$username, - 'password|p=s' => \$password, 'local-addr|l=s' => \$localaddr, 'verbose|v=s' => \$verbose, )) { @@ -502,11 +530,6 @@ if(!GetOptions( exit(1); } -if(!defined $action) { - HELP_MESSAGE(\*STDOUT); - exit(1); -} - if(defined $localaddr) { if($localaddr =~ /(.*):(.*)/) { @@ -520,24 +543,5 @@ if(defined $localaddr) { { initZm(); -# all actions need URI and credentials - $client = ONVIF::Client->new( { - 'url_svc_device' => $url_svc_device, - 'soap_version' => $soap_version } ); - - $client->set_credentials($username, $password, 1); - - $client->create_services(); - - if($action eq "metadata") { - metadata(); - } - elsif($action eq "events") { - - events($localip, $localport); - } - else { - print("Error: Unknown command\"$action\""); - exit(1); - } + events($localip, $localport); }