#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Control Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # ========================================================================== use strict; @EXTRA_PERL_LIB@ use ZoneMinder; use Getopt::Long; use autouse 'Pod::Usage'=>qw(pod2usage); use POSIX qw/strftime EPIPE/; use Socket; #use Data::Dumper; use Module::Load::Conditional qw{can_load};; use constant MAX_CONNECT_DELAY => 15; use constant MAX_COMMAND_WAIT => 1800; $| = 1; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); my $arg_string = join( " ", @ARGV ); my $id; my %options; GetOptions( 'id=i' =>\$id, 'command=s' =>\$options{command}, 'xcoord=i' =>\$options{xcoord}, 'ycoord=i' =>\$options{ycoord}, 'speed=i' =>\$options{speed}, 'step=i' =>\$options{step}, 'panspeed=i' =>\$options{panspeed}, 'tiltspeed=i' =>\$options{tiltspeed}, 'panstep=i' =>\$options{panstep}, 'tiltstep=i' =>\$options{tiltstep}, 'preset=i' =>\$options{preset}, 'autostop' =>\$options{autostop}, ) or pod2usage(-exitstatus => -1); if ( !$id || !$options{command} ) { print( STDERR "Please give a valid monitor id and command\n" ); pod2usage(-exitstatus => -1); } ( $id ) = $id =~ /^(\w+)$/; Debug("zmcontrol: arg string: $arg_string"); my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock'; socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!"); my $saddr = sockaddr_un($sock_file); my $server_up = connect(CLIENT, $saddr); if ( !$server_up ) { # The server isn't there my $monitor = zmDbGetMonitorAndControl($id); if ( !$monitor ) { Fatal("Unable to load control data for monitor $id"); } my $protocol = $monitor->{Protocol}; if ( -x $protocol ) { # Protocol is actually a script! # Holdover from previous versions my $command .= $protocol.' '.$arg_string; Debug($command); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { chomp($output); Debug("Output: $output"); } if ( $status ) { Error("Command '$command' exited with status: $status"); exit($status); } exit(0); } Info("Starting control server $id/$protocol"); close(CLIENT); if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) { Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR"); } if ( my $cpid = fork() ) { logReinit(); # Parent process just sleep and fall through socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or die("Can't open socket: $!"); my $attempts = 0; while ( !connect(CLIENT, $saddr) ) { $attempts++; Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY; sleep(1); } } elsif ( defined($cpid) ) { close(STDOUT); close(STDERR); setpgrp(); logReinit(); Info("Control server $id/$protocol starting at " .strftime('%y/%m/%d %H:%M:%S', localtime()) ); $0 = $0." --id $id"; my $control = "ZoneMinder::Control::$protocol"->new($id); my $control_key = $control->getKey(); $control->loadMonitor(); $control->open(); socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!"); unlink($sock_file); bind(SERVER, $saddr) or Fatal("Can't bind: $!"); listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!"); my $rin = ''; vec( $rin, fileno(SERVER), 1 ) = 1; my $win = $rin; my $ein = $win; my $timeout = MAX_COMMAND_WAIT; while( 1 ) { my $nfound = select(my $rout = $rin, undef, undef, $timeout); if ( $nfound > 0 ) { if ( vec( $rout, fileno(SERVER), 1 ) ) { my $paddr = accept(CLIENT, SERVER); my $message = ; next if !$message; my $params = jsonDecode($message); #Debug( Dumper( $params ) ); my $command = $params->{command}; close( CLIENT ); if ( $command eq 'quit' ) { last; } $control->$command($params); } else { Fatal('Bogus descriptor'); } } elsif ( $nfound < 0 ) { if ( $! == EPIPE ) { Error("Can't select: $!"); } else { Fatal("Can't select: $!"); } } else { #print( "Select timed out\n" ); last; } } # end while forever Info("Control server $id/$protocol exiting"); unlink($sock_file); $control->close(); exit(0); } else { Fatal("Can't fork: $!"); } } # end if !server up # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = jsonEncode(\%options); print(CLIENT $message); shutdown(CLIENT, 1); exit(0); 1; __END__ =head1 NAME zmcontrol.pl - ZoneMinder control script =head1 SYNOPSIS zmcontrol.pl --id {monitor_id} --command={command} [various options] =head1 DESCRIPTION FIXME FIXME =head1 OPTIONS --autostop - --xcoord [ arg ] - X-coord --ycoord [ arg ] - Y-coord --speed [ arg ] - Speed --step [ arg ] - --panspeed [ arg ] - --panstep [ arg ] - --tiltspeed [ arg ] - --tiltstep [ arg ] - --preset [ arg ] - =cut