Merge pull request #879 from SteveGilvarry/feature-h264-videostorage

Merging Master to feature-h264-videostorage
This commit is contained in:
Isaac Connor 2015-06-10 09:30:57 -04:00
commit 564d85161d
232 changed files with 34669 additions and 16567 deletions

View File

@ -32,8 +32,8 @@ install:
- sudo make install-libs
before_script:
- cd $TRAVIS_BUILD_DIR
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then libtoolize --force; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then aclocal; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then libtoolize -v --force; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then aclocal -I m4; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoheader; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then automake --force-missing --add-missing; fi
- if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoconf; fi

View File

@ -1,25 +1,28 @@
# Main CMake file for the ZoneMinder project.
# Created by mastertheknife (Kfir Itzhak)
# The goal is to ease up the installation of zoneminder.
# Our current installation method (using autotools) is outdated, slow and breaks now and then.
# The CMake installation method will require no parameters at all, default should sufficient and reliable.
# It will be still possible to install ZoneMinder using autotools, they don't conflict with each other. The cmake way is a complete re-write (different syntax) and aims to be identical to the autotools way,
# by having options using the same name and leaving ZM totally unmodified, while providing exactly the same things that ZM expects (config.h, configuration in *.in files, etc).
#
# For more information and installation, see the INSTALL file
#
cmake_minimum_required (VERSION 2.6)
project (zoneminder)
set(zoneminder_VERSION "1.28.1")
# CMake does not allow out-of-source build if CMakeCache.exists in the source folder. Abort and notify the user to save him from headache why it doesn't work.
if((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt"))
message(FATAL_ERROR " You are attempting to do an out-of-source build, but a cmake cache file for an in-source build exists. Please delete the file CMakeCache.txt from the source folder to proceed.")
endif((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt"))
# CMake does not allow out-of-source build if CMakeCache.exists
# in the source folder. Abort and notify the user
if(
(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt"))
message(FATAL_ERROR " You are attempting to do an out-of-source build,
but a cmake cache file for an in-source build exists. Please delete
the file CMakeCache.txt from the source folder to proceed.")
endif(
(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt"))
# Default build type. To change the build type, use the CMAKE_BUILD_TYPE configuration option.
# Default build type. To change the build type,
# use the CMAKE_BUILD_TYPE configuration option.
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Release or Debug" FORCE)
set(CMAKE_BUILD_TYPE
Release CACHE STRING "Build type: Release or Debug" FORCE)
endif(NOT CMAKE_BUILD_TYPE)
# Can assist in troubleshooting
@ -42,35 +45,80 @@ include (CheckFunctionExists)
include (CheckPrototypeDefinition_fixed)
include (CheckTypeSize)
include (CheckStructHasMember)
include (CheckSendfile)
# Configuration options
mark_as_advanced(FORCE ZM_EXTRA_LIBS ZM_MYSQL_ENGINE ZM_NO_MMAP CMAKE_INSTALL_FULL_BINDIR ZM_PERL_SUBPREFIX ZM_PERL_USE_PATH ZM_TARGET_DISTRO ZM_CONFIG_DIR)
set(ZM_RUNDIR "/var/run/zm" CACHE PATH "Location of transient process files, default: /var/run/zm")
set(ZM_SOCKDIR "/var/run/zm" CACHE PATH "Location of Unix domain socket files, default /var/run/zm")
set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH "Location of temporary files, default: /tmp/zm")
set(ZM_LOGDIR "/var/log/zm" CACHE PATH "Location of generated log files, default: /var/log/zm")
set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH "Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH "Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH "Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_DB_HOST "localhost" CACHE STRING "Hostname where ZoneMinder database located, default: localhost")
set(ZM_DB_NAME "zm" CACHE STRING "Name of ZoneMinder database, default: zm")
set(ZM_DB_USER "zmuser" CACHE STRING "Name of ZoneMinder database user, default: zmuser")
set(ZM_DB_PASS "zmpass" CACHE STRING "Password of ZoneMinder database user, default: zmpass")
set(ZM_WEB_USER "" CACHE STRING "The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force")
set(ZM_WEB_GROUP "" CACHE STRING "The group apache or the local web server runs on, Leave empty to be the same as the web user")
mark_as_advanced(
FORCE ZM_EXTRA_LIBS
ZM_MYSQL_ENGINE
ZM_NO_MMAP
CMAKE_INSTALL_FULL_BINDIR
ZM_PERL_MM_PARMS
ZM_PERL_SEARCH_PATH
ZM_TARGET_DISTRO
ZM_CONFIG_DIR)
set(ZM_RUNDIR "/var/run/zm" CACHE PATH
"Location of transient process files, default: /var/run/zm")
set(ZM_SOCKDIR "/var/run/zm" CACHE PATH
"Location of Unix domain socket files, default /var/run/zm")
set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH
"Location of temporary files, default: /tmp/zm")
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
"Location of generated log files, default: /var/log/zm")
set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
"Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_DB_HOST "localhost" CACHE STRING
"Hostname where ZoneMinder database located, default: localhost")
set(ZM_DB_NAME "zm" CACHE STRING
"Name of ZoneMinder database, default: zm")
set(ZM_DB_USER "zmuser" CACHE STRING
"Name of ZoneMinder database user, default: zmuser")
set(ZM_DB_PASS "zmpass" CACHE STRING
"Password of ZoneMinder database user, default: zmpass")
set(ZM_WEB_USER "" CACHE STRING
"The user apache or the local web server runs on. Leave empty for automatic detection.
If that fails, you can use this variable to force")
set(ZM_WEB_GROUP "" CACHE STRING
"The group apache or the local web server runs on,
Leave empty to be the same as the web user")
# Advanced
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH "Location of ZoneMinder configuration, default system config directory")
set(ZM_EXTRA_LIBS "" CACHE STRING "A list of optional libraries, separated by semicolons, e.g. ssl;theora")
set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING "MySQL engine to use with database, default: InnoDB")
set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF")
set(ZM_NO_FFMPEG "OFF" CACHE BOOL "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF")
set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF")
set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF")
set(ZM_NO_X10 "OFF" CACHE BOOL "Set to ON to build ZoneMinder without X10 support. default: OFF")
set(ZM_ONVIF "OFF" CACHE BOOL "Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not work with all cameras claiming to be ONVIF compliant. default: OFF")
set(ZM_PERL_SUBPREFIX "${CMAKE_INSTALL_LIBDIR}/perl5" CACHE PATH "Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into <prefix>/lib, default: <libdir>/perl5")
set(ZM_PERL_USE_PATH "${CMAKE_INSTALL_PREFIX}/${ZM_PERL_SUBPREFIX}" CACHE PATH "Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: <prefix>/<zmperlsubprefix>")
set(ZM_TARGET_DISTRO "" CACHE STRING "Build ZoneMinder for a specific distribution. Currently, valid names are: f21, f20, el6, OS13")
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
"Location of ZoneMinder configuration, default system config directory")
set(ZM_EXTRA_LIBS "" CACHE STRING
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING
"MySQL engine to use with database, default: InnoDB")
set(ZM_NO_MMAP "OFF" CACHE BOOL
"Set to ON to not use mmap shared memory. Shouldn't be enabled unless you
experience problems with the shared memory. default: OFF")
set(ZM_NO_FFMPEG "OFF" CACHE BOOL
"Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF")
set(ZM_NO_LIBVLC "OFF" CACHE BOOL
"Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF")
set(ZM_NO_CURL "OFF" CACHE BOOL
"Set to ON to skip cURL checks and force building ZM without cURL. default: OFF")
set(ZM_NO_X10 "OFF" CACHE BOOL
"Set to ON to build ZoneMinder without X10 support. default: OFF")
set(ZM_ONVIF "OFF" CACHE BOOL
"Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
work with all cameras claiming to be ONVIF compliant. default: OFF")
set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING
"By default, ZoneMinder's Perl modules are installed into the Vendor folders,
as defined by your installation of Perl. You can change that here. Consult Perl's
MakeMaker documentation for a definition of acceptable parameters. If you set this
to something that causes the modules to be installed outside Perl's normal search
path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly.")
set(ZM_PERL_SEARCH_PATH "" CACHE PATH
"Use to add a folder to your Perl's search path. This will need to be set in cases
where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are
installed outside Perl's default search path.")
set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: f21, f20, el6, OS13")
# Reassign some variables if a target distro has been specified
if((ZM_TARGET_DISTRO STREQUAL "f21") OR (ZM_TARGET_DISTRO STREQUAL "f20"))
@ -105,14 +153,40 @@ elseif(ZM_TARGET_DISTRO STREQUAL "OS13")
endif((ZM_TARGET_DISTRO STREQUAL "f21") OR (ZM_TARGET_DISTRO STREQUAL "f20"))
# Required for certain checks to work
set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math.h signal.h)
set(CMAKE_EXTRA_INCLUDE_FILES
${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math.h signal.h
)
# Required for including headers from the this folder
include_directories("${CMAKE_BINARY_DIR}")
# This is required to enable searching in lib64 (if exists), do not change
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
# Host OS Check
set(HOST_OS "")
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(HOST_OS "linux")
endif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
set(HOST_OS "solaris")
set(SOLARIS 1)
endif(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
if(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
set(HOST_OS "BSD")
set(BSD 1)
endif(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
set(HOST_OS "darwin")
endif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
if(NOT HOST_OS)
message(FATAL_ERROR
"ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
endif(NOT HOST_OS)
# System checks
check_include_file("linux/videodev.h" HAVE_LINUX_VIDEODEV_H)
check_include_file("libv4l1-videodev.h" HAVE_LIBV4L1_VIDEODEV_H)
if(NOT HAVE_LIBV4L1_VIDEODEV_H)
check_include_file("linux/videodev.h" HAVE_LINUX_VIDEODEV_H)
endif(NOT HAVE_LIBV4L1_VIDEODEV_H)
check_include_file("linux/videodev2.h" HAVE_LINUX_VIDEODEV2_H)
check_include_file("execinfo.h" HAVE_EXECINFO_H)
check_include_file("ucontext.h" HAVE_UCONTEXT_H)
@ -167,10 +241,12 @@ if(JPEG_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${JPEG_INCLUDE_DIR}")
check_include_files("stdio.h;jpeglib.h" HAVE_JPEGLIB_H)
if(NOT HAVE_JPEGLIB_H)
message(FATAL_ERROR " zm requires libjpeg headers - check that libjpeg development packages are installed")
message(FATAL_ERROR
"ZoneMinder requires libjpeg headers - check that libjpeg development packages are installed")
endif(NOT HAVE_JPEGLIB_H)
else(JPEG_FOUND)
message(FATAL_ERROR "zm requires jpeg but it was not found on your system")
message(FATAL_ERROR
"ZoneMinder requires jpeg but it was not found on your system")
endif(JPEG_FOUND)
# OpenSSL
@ -200,10 +276,12 @@ if(PTHREAD_LIBRARIES)
mark_as_advanced(FORCE PTHREAD_LIBRARIES PTHREAD_INCLUDE_DIR)
check_include_file("pthread.h" HAVE_PTHREAD_H)
if(NOT HAVE_PTHREAD_H)
message(FATAL_ERROR " zm requires pthread headers - check that pthread development packages are installed")
message(FATAL_ERROR
"ZoneMinder requires pthread headers - check that pthread development packages are installed")
endif(NOT HAVE_PTHREAD_H)
else(PTHREAD_LIBRARIES)
message(FATAL_ERROR "zm requires pthread but it was not found on your system")
message(FATAL_ERROR
"ZoneMinder requires pthread but it was not found on your system")
endif(PTHREAD_LIBRARIES)
# pcre (using find_library and find_path)
@ -275,10 +353,12 @@ if(MYSQLCLIENT_LIBRARIES)
mark_as_advanced(FORCE MYSQLCLIENT_LIBRARIES MYSQLCLIENT_INCLUDE_DIR)
check_include_file("mysql/mysql.h" HAVE_MYSQL_H)
if(NOT HAVE_MYSQL_H)
message(FATAL_ERROR "zm requires MySQL headers - check that MySQL development packages are installed")
message(FATAL_ERROR
"ZoneMinder requires MySQL headers - check that MySQL development packages are installed")
endif(NOT HAVE_MYSQL_H)
else(MYSQLCLIENT_LIBRARIES)
message(FATAL_ERROR "zm requires mysqlclient but it was not found on your system")
message(FATAL_ERROR
"ZoneMinder requires mysqlclient but it was not found on your system")
endif(MYSQLCLIENT_LIBRARIES)
# x264 (using find_library and find_path)
@ -458,7 +538,8 @@ endif(NOT ZM_NO_LIBVLC)
# Check for gnutls or crypto
if((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS))
message(FATAL_ERROR " zm requires crypto or gnutls but none were found on your system")
message(FATAL_ERROR
"ZoneMinder requires crypto or gnutls but none were found on your system")
endif((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS))
# Check for V4L header files and enable ZM_HAS_V4L, ZM_HAS_V4L1, ZM_HAS_V4L2 accordingly
@ -466,17 +547,22 @@ endif((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS))
set(ZM_HAS_V4L 0)
set(ZM_HAS_V4L1 0)
set(ZM_HAS_V4L2 0)
if(HAVE_LINUX_VIDEODEV_H)
if(HAVE_LINUX_VIDEODEV_H OR HAVE_LIBV4L1_VIDEODEV_H)
set(ZM_HAS_V4L 1)
set(ZM_HAS_V4L1 1)
endif(HAVE_LINUX_VIDEODEV_H)
endif(HAVE_LINUX_VIDEODEV_H OR HAVE_LIBV4L1_VIDEODEV_H)
if(HAVE_LINUX_VIDEODEV2_H)
set(ZM_HAS_V4L 1)
set(ZM_HAS_V4L2 1)
endif(HAVE_LINUX_VIDEODEV2_H)
if((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H))
message(AUTHOR_WARNING " Video 4 Linux headers weren't found - Analog and USB camera support will not be available")
endif((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H))
if((NOT HAVE_LINUX_VIDEODEV_H)
AND (NOT HAVE_LIBV4L1_VIDEODEV_H)
AND (NOT HAVE_LINUX_VIDEODEV2_H))
message(AUTHOR_WARNING
"Video 4 Linux headers weren't found - Analog and USB camera support will not be available")
endif((NOT HAVE_LINUX_VIDEODEV_H)
AND (NOT HAVE_LIBV4L1_VIDEODEV_H)
AND (NOT HAVE_LINUX_VIDEODEV2_H))
# Check for PCRE and enable ZM_PCRE accordingly
set(ZM_PCRE 0)
if(HAVE_LIBPCRE AND HAVE_PCRE_H)
@ -497,26 +583,37 @@ if(ZM_ONVIF)
set(ZM_HAS_ONVIF 1)
endif(ZM_ONVIF)
# Check for authenication functions
# Check for authentication functions
if(HAVE_OPENSSL_MD5_H)
set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
check_prototype_definition(MD5 "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL)
check_prototype_definition(
MD5
"unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h"
HAVE_MD5_OPENSSL)
endif(HAVE_OPENSSL_MD5_H)
if(HAVE_GNUTLS_OPENSSL_H)
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
check_prototype_definition(MD5 "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" HAVE_MD5_GNUTLS)
check_prototype_definition(
MD5
"unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h"
HAVE_MD5_GNUTLS)
endif(HAVE_GNUTLS_OPENSSL_H)
if(HAVE_GNUTLS_GNUTLS_H)
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
check_prototype_definition(gnutls_fingerprint "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT)
check_prototype_definition(
gnutls_fingerprint
"int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h"
HAVE_DECL_GNUTLS_FINGERPRINT)
endif(HAVE_GNUTLS_GNUTLS_H)
if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
set(HAVE_DECL_MD5 1)
else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
message(AUTHOR_WARNING " ZM requires a working MD5 function for hashed authenication but none were found - hashed authenication will not be available")
message(AUTHOR_WARNING
"ZoneMinder requires a working MD5 function for hashed authenication but
none were found - hashed authenication will not be available")
endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
# Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available.
# This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac
@ -528,26 +625,40 @@ endif(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
# Check for Perl
find_package(Perl)
if(NOT PERL_FOUND)
message(FATAL_ERROR "zm requires Perl 5.6.0 or newer but it was not found on your system")
message(FATAL_ERROR
"ZoneMinder requires Perl 5.6.0 or newer but it was not found on your system")
endif(NOT PERL_FOUND)
# Checking for perl modules requires FindPerlModules.cmake
# Check all required modules at once
# TODO: Add checking for the optional modules
find_package(PerlModules COMPONENTS Sys::Syslog DBI DBD::mysql Getopt::Long Time::HiRes Date::Manip LWP::UserAgent ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE})
find_package(
PerlModules COMPONENTS Sys::Syslog DBI DBD::mysql
Getopt::Long Time::HiRes Date::Manip LWP::UserAgent
ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE})
if(NOT PERLMODULES_FOUND)
message(FATAL_ERROR "Not all required perl modules were found on your system")
message(FATAL_ERROR
"Not all required perl modules were found on your system")
endif(NOT PERLMODULES_FOUND)
# Attempt to check which user apache (or other web server) runs on by searching for a user beginning with apache or www and then cutting the user from the first matching user line
# Attempt to check which user apache (or other web server) runs on by
# searching for a user beginning with apache or www and then cutting the user
# from the first matching user line
if(ZM_WEB_USER STREQUAL "")
# Check for a user matching ^apache and cut the username from the userline in the first match
# Check for a user matching ^apache and cut the username from the
# userline in the first match
file(STRINGS "/etc/passwd" userline_apache REGEX "^apache")
file(STRINGS "/etc/passwd" userline_www REGEX "^www")
if(NOT (userline_apache STREQUAL ""))
execute_process(COMMAND echo ${userline_apache} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND echo ${userline_apache}
COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER
OUTPUT_STRIP_TRAILING_WHITESPACE)
elseif(NOT (userline_www STREQUAL ""))
execute_process(COMMAND echo ${userline_www} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND echo ${userline_www}
COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif(NOT (userline_apache STREQUAL ""))
message(STATUS "Detected web server user: ${ZM_WEB_USER}")
endif(ZM_WEB_USER STREQUAL "")
@ -561,7 +672,8 @@ message(STATUS "Using web group: ${ZM_WEB_GROUP}")
# Check for polkit
find_package(Polkit)
if(NOT POLKIT_FOUND)
message(FATAL_ERROR "Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.")
message(FATAL_ERROR
"Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.")
endif(NOT POLKIT_FOUND)
# Some variables that zm expects
@ -577,7 +689,11 @@ set(CGI_PREFIX "${ZM_CGIDIR}")
set(WEB_USER "${ZM_WEB_USER}")
set(WEB_GROUP "${ZM_WEB_GROUP}")
set(ZM_DB_TYPE "mysql")
set(EXTRA_PERL_LIB "use lib '${ZM_PERL_USE_PATH}';")
if(ZM_PERL_SEARCH_PATH)
set(EXTRA_PERL_LIB "use lib '${ZM_PERL_SEARCH_PATH}'; # Include custom perl install path")
else(ZM_PERL_SEARCH_PATH)
set(EXTRA_PERL_LIB "# Include from system perl paths only")
endif(ZM_PERL_SEARCH_PATH)
# Generate files from the .in files
configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY)
@ -593,9 +709,9 @@ add_subdirectory(web)
add_subdirectory(misc)
# Enable ONVIF support
if (ZM_ONVIF)
if(ZM_ONVIF)
add_subdirectory(onvif)
endif (ZM_ONVIF)
endif(ZM_ONVIF)
# Process distro subdirectories
if((ZM_TARGET_DISTRO STREQUAL "f21") OR (ZM_TARGET_DISTRO STREQUAL "f20"))
@ -614,9 +730,11 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}")
message(STATUS "Running ZoneMinder configuration generator")
execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result)
if(zmconfgen_result EQUAL 0)
message(STATUS "ZoneMinder configuration generator completed successfully")
message(STATUS
"ZoneMinder configuration generator completed successfully")
else(zmconfgen_result EQUAL 0)
message(FATAL_ERROR "ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}")
message(FATAL_ERROR
"ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}")
endif(zmconfgen_result EQUAL 0)
# Install zm.conf

20
INSTALL
View File

@ -53,8 +53,8 @@ Advanced:
ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
ZM_NO_FFMPEG Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF
ZM_NO_X10 Set to ON to build ZoneMinder without X10 support. default: OFF
ZM_PERL_SUBPREFIX Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. /lib will be turned into <prefix>/lib, default: <libarch>/perl5
ZM_PERL_USE_PATH Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: <prefix>/<zmperlsubprefix>
ZM_PERL_MM_PARMS By default, ZoneMinder's Perl modules are installed into the Vendor folders, as defined by your installation of Perl. You can change that here. Consult Perl's MakeMaker documentation for a definition of acceptable parameters. If you set this to something that causes the modules to be installed outside Perl's normal serach path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly. default: "INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1"
ZM_PERL_SEARCH_PATH Use to add a folder to your Perl's search path. This will need to be set in cases where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are installed outside Perl's default search path. default: ""
Useful configuration options provided by cmake:
CMAKE_VERBOSE_MAKEFILE - Set this to ON (default OFF) to see what cmake is doing. Very useful for troubleshooting.
@ -93,9 +93,19 @@ NOTE: The database server, database name, user and password can be different and
8) Create an apache virtual host for ZoneMinder. Make sure to use the same paths as ZM_WEBDIR and ZM_CGIDIR in /etc/zm.conf
9) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in <prefix>/share/zoneminder/misc or project/misc directory
10) Setup an appropriate startup script for your system. A generic sys v init script is here: /scripts/zm while a generic systemd service file is here: /misc/zoneminder.service
You must determine which file to use, verify it is correct, and then copy it to the correct location. Consult your distro's documentation. Note that distros using systemd also
require /misc/zoneminder-tmpfiles.conf to be copied into the system's tmpfiles.d folder.
10) Setup an appropriate startup script for your system. Two generic startup scripts have been provided, a legacy Sys V Init script and a Systemd service file.
*Sys V Init Setup*
- Copy the sys v init script /scripts/zm from the build folder to /etc/init.
- Inspect the contents to make sure they apply to your distro.
*SystemD Setup*
- Copy the zoneminder systemd service file /misc/zoneminder.service from the build folder to the systemd service file location.
For Redhat based distros, that folder is /usr/lib/systemd/system.
- Inspect the contents to make sure they apply to your distro.
- Tell systemd to load the new service file: "sudo systemctl daemon-reload".
- Copy /misc/zoneminder-tmpfiles.conf to /etc/tmpfiles.d
- Tell systemd to process this file: "sudo /usr/bin/systemd-tmpfiles --create /etc/tmpfiles.d/zoneminder.conf".
Basic steps for upgrading ZoneMinder
------------------------------------

View File

@ -1,4 +1,5 @@
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
# And these to the user and group of your webserver
webuser = @WEB_USER@

182
README.md
View File

@ -12,13 +12,46 @@ All documentation for ZoneMinder is now online at http://www.zoneminder.com/wiki
ZoneMinder is an integrated set of applications which provide a complete surveillance solution allowing capture, analysis, recording and monitoring of any CCTV or security cameras attached to a Linux based machine. It is designed to run on distributions which support the Video For Linux (V4L) interface and has been tested with video cameras attached to BTTV cards, various USB cameras and also supports most IP network cameras.
## Requirements
## Installation Methods
If you are installing ZoneMinder from a package, that package should provide all of the needed core components.
### Building from Source is Discouraged
### Packages
Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again.
If you are compiling ZoneMinder from source, the below list contains the packages needed to get ZoneMinder built:
Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source.
### Install from a Package Repository
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/)
- Fedora via [zmrepo](http://zmrepo.zoneminder.com/)
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
- Maegia from their default repository
If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own.
### Building a ZoneMinder Package
Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier.
When building a package, it is best to do this work in a separate environment, dedicated to development purposes. This could be as simple as creating a virtual machine, using Docker, or using mock. All it takes is one “Oops” to regret doing this work on your production server.
Lastly, if you desire to build a development snapshot from the master branch, it is recommended you first build your package using an official release of ZoneMinder. This will help identify whether any problems you may encounter are caused by the build process or is a new issue in the master branch.
What follows are instructions for various distros to build ZoneMinder into a package.
### Package Maintainters
Many of the ZoneMinder configration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source.
For example, let's say I have created a new ZoneMinder package that contains the cambolzola javascript file. However, by default cambozola support is turned off. To fix that, add this to the pacakging script:
```bash
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
```
Note that zmeditconfigdata.sh is intended to be called, from the root build folder, prior to running cmake or configure.
#### Ubuntu
@ -104,42 +137,118 @@ root@host:~# aptitude install -y gdebi;
root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb;
```
#### CentOS / RHEL
#### Fedora / CentOS / RHEL
Additional repositories must be added before one can build zoneminder on CentOS or RHEL:
***DRAFT* ** Needs Testing
1. Zmrepo [ZoneMinder WiKi](http://www.zoneminder.com/wiki/index.php/CentOS#Zmrepo_-_A_ZoneMinder_repository_for_RPM_based_distros)
2. EPEL https://fedoraproject.org/wiki/EPEL
3. RPMFusion: http://rpmfusion.org/
##### Background
The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
When adding third party repositories, it is highly recommended that the user also install and configure yum priorities as documented in the [CentOS WiKi](http://wiki.centos.org/PackageManagement/Yum/Priorities)
The method documented below was chosen because:
- All of ZoneMinder's dependencies are downloaded and installed automatically
- Cross platform capable. The build host does not have to be the same distro or release version as the target.
- Once your build environment is set up, few steps are required to run the build again in the future.
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
Prioritize the repositories:
The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire.
1. Base
2. EPEL
3. RPMFusion
4. Zmrepo
***IMPORTANT***
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build.
Once your repos are in order, install the following:
```bash
sudo yum install cmake bzip2-devel ffmpeg ffmpeg-devel gnutls-devel httpd libjpeg-turbo libjpeg-turbo-devel mysql-devel mysql-server pcre-devel \
perl-Archive-Tar perl-Archive-Zip perl-Convert-BinHex perl-Date-Manip perl-DBD-MySQL perl-DBI perl-Device-SerialPort perl-Email-Date-Format perl-IO-stringy \
perl-IO-Zlib perl-MailTools perl-MIME-Lite perl-MIME-tools perl-MIME-Types perl-Module-Load perl-Package-Constants perl-Sys-Mmap perl-Time-HiRes \
perl-TimeDate perl-YAML-Syck perl-X10 perl-URI-Encode php php-cli php-mysql x264 vlc-devel vlc-core \
libcurl libcurl-devel polkit-devel git
##### Set Up Your Environment
Before you begin, set up an rpmbuild environment by following [this guide](http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment) by the CentOS developers.
Next, navigate to [Zmrepo](http://zmrepo.zoneminder.com/), and follow the instructions to enable zmrepo on your system.
With zmrepo enabled, issue the following command:
````bash
sudo yum install zmrepo-mock-configs mock
```
To build from the master branch:
Add your user account to the group mock:
```bash
git clone https://github.com/ZoneMinder/ZoneMinder.git
cd ZoneMinder
cmake .
make
sudo make install
sudo gpasswd -a {your account name} mock
```
IMPORTANT: Don't forget the trailing "." when calling cmake
Your build environment is now set up.
##### Build from SRPM
To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the [Zmrepo](http://zmrepo.zoneminder.com/) site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder.
For CentOS 7, I have chosen the following SRPM:
```bash
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
```
Now comes the fun part. To build ZoneMinder, issue the following command:
```bash
buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
```
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
```bash
buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm
```
Notice that the buildzm.sh tool requires the following parameters:
```bash
buildzm.sh MOCKCONFIG ZONEMINDER_SRPM
```
The list of available Mock config files are available here:
```bash
ls /etc/mock/zmrepo*.cfg
```
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
##### Installation
Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the [Zmrepo](http://zmrepo.zoneminder.com/) website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
##### How to Modify the Source Prior to Build
** UNFINISHED **
Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong.
These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch.
From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system:
```bash
rpm -Uvh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
```
IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root.
Make sure you have git installed:
```bash
sudo yum install git
```
Now clone the ZoneMinder git repository:
```bash
git clone https://github.com/ZoneMinder/ZoneMinder
```
This will create a sub-folder called ZoneMinder, which will contain the latest developement.
We want to turn this into a tarball, but first we need to figure out what to name it. Look here:
```bash
ls ~/rpmbuild/SOURCES
```
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From one folder above the local ZoneMinder git repository, execute the following:
```bash
mv ZoneMinder ZoneMinder-1.28.1
tar -cvzf ~/rpmbuild/SOURCES/ZoneMinder-1.28.1.tar.gz ZoneMinder-1.28.1/*
```
The trailing "/*" leaves off the hidden dot "." file and folders from the git repo, which is what we want.
Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball.
Now build a new src.rpm:
```bash
rpmbuild -bs --nodeps ~/rpmbuild/SPECS/zoneminder.el7.spec
```
This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on what distro you are building for.
You should now have a a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM.
#### Docker
@ -147,11 +256,6 @@ Docker is a system to run applications inside isolated containers. ZoneMinder, a
Dockerfile contained in this repository. However, there is still work needed to ensure that the main ZM features work
properly and are documented.
### ffmpeg
This release of ZoneMinder has been tested on and works with ffmpeg version N-55540-g93f4277.
## Contribution Model and Development
* Source hosted at [GitHub](https://github.com/ZoneMinder/ZoneMinder/)
@ -172,14 +276,4 @@ the following steps.
6. Create new Pull Request
7. The team will then review, discuss and hopefully merge your changes.
### Package Maintainters
Many of the ZoneMinder configration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source.
For example, let's say I have created a new ZoneMinder package that contains the cambolzola javascript file. However, by default cambozola support is turned off. To fix that, add this to the pacakging script:
```bash
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
```
Note that zmeditconfigdata.sh is intended to be called, from the root build folder, prior to running cmake or configure.
[![Analytics](https://ga-beacon.appspot.com/UA-15147273-6/ZoneMinder/README.md)](https://github.com/igrigorik/ga-beacon)

View File

@ -1,5 +1,5 @@
#!/bin/bash
aclocal
aclocal -I m4
autoheader
automake --add-missing
autoconf

View File

@ -0,0 +1,58 @@
# Check whether sendfile() is supported and what prototype it has
include(CheckCSourceCompiles)
if (UNIX OR MINGW)
SET(CMAKE_REQUIRED_DEFINITIONS -Werror-implicit-function-declaration)
endif()
check_c_source_compiles("#include <sys/sendfile.h>
#include <stdio.h>
int main()
{
sendfile(1, 1, NULL, 0);
return 0;
}" HAVE_SENDFILE4_SUPPORT)
if(HAVE_SENDFILE4_SUPPORT)
add_definitions(-DHAVE_SENDFILE4_SUPPORT=1)
unset(CMAKE_REQUIRED_DEFINITIONS)
message(STATUS "Sendfile support: Linux/Solaris sendfile()")
return()
endif()
find_library(SENDFILE_LIBRARIES NAMES sendfile)
if(SENDFILE_LIBRARIES)
include(CheckLibraryExists)
check_library_exists(sendfile sendfile ${SENDFILE_LIBRARIES} HAVE_SENDFILE4_SUPPORT)
if(HAVE_SENDFILE4_SUPPORT)
add_definitions(-DHAVE_SENDFILE4_SUPPORT=1)
unset(CMAKE_REQUIRED_DEFINITIONS)
message(STATUS "Sendfile support: Solaris sendfile()")
return()
endif()
endif()
set(SENDFILE_LIBRARIES "")
check_c_source_compiles("#include <sys/socket.h>
#include <stdio.h>
int main()
{
sendfile(1, 1, 0, 0, NULL, NULL, 0);
return 0;
}" HAVE_SENDFILE7_SUPPORT)
if(HAVE_SENDFILE7_SUPPORT)
add_definitions(-DHAVE_SENDFILE7_SUPPORT=1)
unset(CMAKE_REQUIRED_DEFINITIONS)
message(STATUS "Sendfile support: FreeBSD sendfile()")
return()
endif()
check_c_source_compiles("#include <sys/socket.h>
#include <stdio.h>
#include <sys/uio.h>
int main()
{
sendfile(1, 1, 0, NULL, NULL, 0);
return 0;
}" HAVE_SENDFILE6_SUPPORT)
if(HAVE_SENDFILE6_SUPPORT)
add_definitions(-DHAVE_SENDFILE6_SUPPORT=1)
unset(CMAKE_REQUIRED_DEFINITIONS)
message(STATUS "Sendfile support: MacOS sendfile()")
return()
endif()

View File

@ -3,6 +3,7 @@ AC_INIT(zm,1.28.1,[http://www.zoneminder.com/forums/ - Please check FAQ first],z
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR(src/zm.h)
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_MACRO_DIR([m4])
AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS])
@ -26,6 +27,17 @@ case $host_os in
*BSD*)
# Do something specific for BSD
HOST_OS='BSD'
AC_DEFINE(BSD,1,"This is a BSD system")
;;
*bsd*)
# Do something specific for BSD
HOST_OS='BSD'
AC_DEFINE(BSD,1,"This is a BSD system")
;;
*solaris*)
# Do something specific for Solaris
HOST_OS='solaris'
AC_DEFINE(SOLARIS,1,"We are running a Solaroid OS [tested on OmniOS]")
;;
*)
#Default Case
@ -315,22 +327,20 @@ AC_FUNC_STRTOD
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([gethostbyname gethostname gettimeofday memmove memset mkdir munmap posix_memalign putenv select sendfile socket sqrt strcasecmp strchr strcspn strerror strncasecmp strrchr strspn strstr strtol strtoull])
AC_CHECK_FUNCS([syscall sleep usleep ioctl ioctlsocket sigaction])
# this is required for freebsd to compile. Look for it in m4/ac_check_sendfile.m4
AC_CHECK_SENDFILE
# Other programs
AC_CHECK_PROG(OPT_FFMPEG,ffmpeg,yes,no)
AC_PATH_PROG(PATH_FFMPEG,ffmpeg)
AC_CHECK_PROG(OPT_NETPBM,pnmscale,yes,no)
AC_PATH_PROG(PATH_NETPBM,pnmscale)
if test "$OPT_NETPBM" == "yes"; then
PATH_NETPBM=`dirname $PATH_NETPBM`
fi
# Checks for libraries.
AC_CHECK_LIB(rt,clock_gettime,,AC_MSG_ERROR(zm requires librt))
AC_SEARCH_LIBS(mysql_init,[mysqlclient mariadbclient],,AC_MSG_ERROR(zm requires libmysqlclient.a or libmariadbclient.a))
AC_CHECK_LIB(jpeg,jpeg_start_compress,,AC_MSG_ERROR(zm requires libjpeg.a))
AC_CHECK_LIB(pthread,pthread_create,,AC_MSG_ERROR(zm requires libpthread.a))
if test "$BSD" == "0"; then
AC_CHECK_LIB(dl,dlsym,,AC_MSG_ERROR(zm requires libdl.a))
fi
if test "$ZM_SSL_LIB" == "openssl"; then
AC_CHECK_HEADERS(openssl/md5.h,,AC_MSG_WARN(zm requires openssl/md5.h header to be installed for openssl),)
AC_CHECK_LIB(crypto,MD5,,AC_MSG_WARN([libcrypto.a is required for authenticated streaming - use ZM_SSL_LIB option to select gnutls instead]))
@ -374,14 +384,26 @@ AC_CHECK_HEADERS(execinfo.h,,,)
AC_CHECK_HEADERS(ucontext.h,,,)
AC_CHECK_HEADERS(sys/syscall.h,,,)
AC_CHECK_HEADERS(pthread.h,,,)
AC_CHECK_HEADERS(linux/videodev.h,AC_SUBST(ZM_HAS_V4L1,1),AC_SUBST(ZM_HAS_V4L1,0),)
AC_CHECK_HEADERS(linux/videodev2.h,AC_SUBST(ZM_HAS_V4L2,1),AC_SUBST(ZM_HAS_V4L2,0),)
# Check for Video for Linux 1 Header Files
ZM_HAS_V4L1=0
AC_CHECK_HEADERS([libv4l1-videodev.h linux/videodev.h],[ZM_HAS_V4L1=1; break;],,)
AC_SUBST(ZM_HAS_V4L1)
# Check for Video for Linux 2 Header Files
ZM_HAS_V4L2=0
AC_CHECK_HEADERS(linux/videodev2.h,ZM_HAS_V4L2=1,,)
AC_SUBST(ZM_HAS_V4L2)
# Set global Video for Linux flag
ZM_HAS_V4L=0
if test "$ZM_HAS_V4L1" == "1" || test "$ZM_HAS_V4L2" == "1"; then
AC_SUBST(ZM_HAS_V4L,1)
ZM_HAS_V4L=1
else
AC_SUBST(ZM_HAS_V4L,0)
AC_MSG_WARN(zm requires Video4Linux or Video4Linux2 to be installed for analog or USB camera support)
fi
AC_SUBST(ZM_HAS_V4L)
AC_CHECK_HEADERS(jpeglib.h,,AC_MSG_ERROR(zm requires libjpeg headers to be installed),)
AC_CHECK_HEADERS(mysql/mysql.h,,AC_MSG_ERROR(zm requires MySQL headers - check that MySQL development packages are installed),)
AC_LANG_PUSH([C])

View File

@ -108,4 +108,9 @@ SELECT * FROM (SELECT NULL as Id,
WHERE NOT EXISTS (
SELECT Name FROM Controls WHERE name = 'ONVIF Camera'
) LIMIT 1;
--
-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss
--
UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE';

View File

@ -22,7 +22,6 @@ override_dh_auto_configure:
-DZM_CGIDIR=/usr/lib/cgi-bin \
-DZM_WEB_USER=www-data \
-DZM_WEB_GROUP=www-data \
-DZM_PERL_SUBPREFIX=/share/perl5 \
-DCMAKE_INSTALL_SYSCONFDIR=etc/zm
override_dh_auto_install:

View File

@ -32,7 +32,7 @@ install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DE
# Install auxillary files required to run zoneminder on Fedora
install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ../../misc/zoneminder-tmpfiles.conf DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)

View File

@ -72,7 +72,6 @@ too much degradation of performance.
%build
%cmake \
-DZM_TARGET_DISTRO="f19" \
-DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \
%{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \
%{?_without_x10:-DZM_NO_X10=ON} \
.

View File

@ -31,7 +31,7 @@ BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap)
BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign)
BuildRequires: perl(Expect) perl(Sys::Syslog)
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel
%{!?_without_ffmpeg:BuildRequires: ffmpeg-devel}
%{!?_without_x10:BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime)}
# cmake needs the following installed at build time due to the way it auto-detects certain parameters
@ -76,7 +76,6 @@ too much degradation of performance.
%build
%cmake \
-DZM_TARGET_DISTRO="f20" \
-DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \
%{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \
%{?_without_x10:-DZM_NO_X10=ON} \
.

View File

@ -31,7 +31,7 @@ BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap)
BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign)
BuildRequires: perl(Expect) perl(Sys::Syslog)
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel
%{!?_without_ffmpeg:BuildRequires: ffmpeg-devel}
%{!?_without_x10:BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime)}
# cmake needs the following installed at build time due to the way it auto-detects certain parameters
@ -76,7 +76,6 @@ too much degradation of performance.
%build
%cmake \
-DZM_TARGET_DISTRO="f21" \
-DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \
%{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \
%{?_without_x10:-DZM_NO_X10=ON} \
.
@ -155,8 +154,6 @@ fi
%{!?_without_x10:%{_bindir}/zmx10.pl}
%{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ZoneMinder*
#%{perl_archlib}/ZoneMinder*
%{_mandir}/man*/*
%dir %{_libexecdir}/zoneminder
%{_libexecdir}/zoneminder/cgi-bin

View File

@ -58,7 +58,7 @@ if(ZM_TARGET_DISTRO STREQUAL "el7")
install(FILES zoneminder.el7.conf DESTINATION /etc/httpd/conf.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.el7.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ../../misc/zoneminder-tmpfiles.conf DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
else(ZM_TARGET_DISTRO STREQUAL "el7")
install(FILES zoneminder.el6.conf DESTINATION /etc/httpd/conf.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.el6.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)

View File

@ -73,7 +73,7 @@ too much degradation of performance.
%build
# Have to override CMAKE_INSTALL_LIBDIR for cmake < 2.8.7 due to this bug:
# https://bugzilla.redhat.com/show_bug.cgi?id=795542
%cmake -DZM_TARGET_DISTRO="el6" -DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` .
%cmake -DZM_TARGET_DISTRO="el6" -DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} .
make %{?_smp_mflags}

View File

@ -27,7 +27,7 @@ BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap)
BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign)
BuildRequires: perl(Expect) perl(Sys::Syslog)
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel
BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel
BuildRequires: ffmpeg ffmpeg-devel perl(X10::ActiveHome) perl(Astro::SunTime)
# cmake needs the following installed at build time due to the way it auto-detects certain parameters
BuildRequires: httpd polkit-devel
@ -69,7 +69,6 @@ too much degradation of performance.
%build
%cmake \
-DZM_TARGET_DISTRO="el7" \
-DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \
.
make %{?_smp_mflags}
@ -108,6 +107,8 @@ if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
/bin/systemctl --no-reload disable 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
%postun
@ -129,7 +130,8 @@ fi
%files
%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(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf
%config(noreplace) /etc/tmpfiles.d/zoneminder.conf
@ -157,7 +159,8 @@ fi
%{_bindir}/zmx10.pl
%{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*
%{_mandir}/man*/*
%dir %{_libexecdir}/zoneminder

View File

@ -0,0 +1,51 @@
zoneminder for Debian
---------------------
There is one manual step to get the web interface working.
You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf,
then reload the apache config (i.e. /etc/init.d/apache2 reload)
Changing the location for images and events
-------------------------------------------
Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This
package modifies that by changing /usr/share/zoneminder/images and
/usr/share/zoneminder/events to symlinks to directories under
/var/cache/zoneminder.
There are numerous places these could be put and ways to do it. But, at the
moment, if you change this, an upgrade will fail with a warning about these
locations having changed (the reason for this was that previously, an upgrade
would silently revert the changes and cause event loss - refer
bug #608793).
If you do want to change the location, here are a couple of suggestions.
(thanks to vagrant@freegeek.org):
These lines in fstab could allow you to bind-mount an alternate location
/dev/sdX1 /otherdrive ext3 defaults 0 2
/otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2
/otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2
or if you have a separate partition for each:
/dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2
/dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2
-- Peter Howard <pjh@northern-ridge.com.au>, Sun, 16 Jan 2010 01:35:51 +1100
Access to /dev/video*
---------------------
For cameras which require access to /dev/video*, zoneminder may need the
www-data user added to the video group in order to see those cameras:
adduser www-data video
Note that all web applications running on the zoneminder server will then have
access to all video devices on the system.
-- Vagrant Cascadian <vagrant@debian.org> Sun, 27 Mar 2011 13:06:56 -0700

View File

@ -0,0 +1,9 @@
Alias /zm /usr/share/zoneminder
<Directory /usr/share/zoneminder>
php_flag register_globals off
Options Indexes FollowSymLinks
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>
</Directory>

View File

@ -0,0 +1,86 @@
zoneminder (1.28.1+1-trusty-SNAPSHOT2015030201) trusty; urgency=medium
* maybe fix for RTSP Basic Auth
* Also remove dependency on netpbm
-- Isaac Connor <iconnor@connortechnology.com> Mon, 02 Mar 2015 11:25:59 -0500
zoneminder (1.28.1+1-utopic-SNAPSHOT2015022301) utopic; urgency=medium
* Big merge of onvif support and some fixes.
-- Isaac Connor <iconnor@connortechnology.com> Mon, 23 Feb 2015 19:45:45 -0500
zoneminder (1.28.0+1-trusty-SNAPSHOT2015021201) trusty; urgency=medium
* add mysql-client-5.6 as a dependency instaed of mysql-client.
-- Isaac Connor <iconnor@connortechnology.com> Fri, 13 Feb 2015 09:35:13 -0500
zoneminder (1.28.0+1-trusty-SNAPSHOT2015011101) trusty; urgency=medium
* small changes
-- Isaac Connor <iconnor@connortechnology.com> Fri, 12 Dec 2014 16:38:36 -0500
zoneminder (1.28.0+1-utopic-SNAPSHOT2014112001) utopic; urgency=medium
* Various fixes and developments since 1.28.0. Includes Digest-Auth for HTTP and better for RTSP
-- Isaac Connor <iconnor@connortechnology.com> Thu, 20 Nov 2014 10:57:57 -0500
zoneminder (1.28.0-trusty) trusty; urgency=medium
* Release
-- Isaac Connor <iconnor@connortechnology.com> Fri, 17 Oct 2014 09:25:55 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014101401) trusty; urgency=medium
* Several PR merges in big push for 1.28.0
-- Isaac Connor <iconnor@connortechnology.com> Tue, 14 Oct 2014 09:28:29 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014092601) trusty; urgency=medium
* style updates and db fixes for database logging filters
-- Isaac Connor <iconnor@connortechnology.com> Fri, 26 Sep 2014 14:44:45 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014090801) trusty; urgency=medium
* several segfault fixes for local cameras
-- Isaac Connor <iconnor@connortechnology.com> Mon, 08 Sep 2014 16:56:57 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014090701) trusty; urgency=medium
* Fix segfaults for local cameras, also now includes the systemd support patch
-- Isaac Connor <iconnor@connortechnology.com> Sun, 07 Sep 2014 17:19:01 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014082102) trusty; urgency=medium
* Fix UI inputs for v4l multibuffer and captures per frame
-- Isaac Connor <iconnor@connortechnology.com> Thu, 21 Aug 2014 12:03:31 -0400
zoneminder (1.27.99+1-trusty-SNAPSHOT2014082101) trusty; urgency=medium
* fall back to Config table values for V4l MultiBUffer and Captures Per Frame
* add mention of monitor page settings for thse in the config table
-- Isaac Connor <iconnor@connortechnology.com> Thu, 21 Aug 2014 10:04:46 -0400
zoneminder (1.27.99+1-precise-SNAPSHOT2014080601) precise; urgency=medium
* improve error messages
* Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates
-- Isaac Connor <iconnor@connortechnology.com> Wed, 06 Aug 2014 20:20:20 -0400
zoneminder (1.27.0+1-trusty-v4ltomonitor-1) trusty; urgency=high
* Snapshot release -
-- Isaac Connor <iconnor@connortechnology.com> Wed, 09 Jul 2014 21:35:29 -0400

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,40 @@
Source: zoneminder
Section: net
Priority: optional
Maintainer: Isaac Connor <iconnor@connortechnology.com>
Build-Depends: debhelper (>= 9), dh-systemd (>= 1.5), autoconf, automake, quilt, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, libpcre3-dev, libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, ffmpeg, libavdevice-ffmpeg-dev, libdevice-serialport-perl, libpcre3, libarchive-zip-perl, libmime-lite-perl, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev, libpolkit-gobject-1-dev
Standards-Version: 3.9.4
Package: zoneminder
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client|mysql-client-5.6, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libavdevice-ffmpeg56, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl
Recommends: mysql-server|mariadb-server
Description: Video camera security and surveillance solution
ZoneMinder is intended for use in single or multi-camera video security
applications, including commercial or home CCTV, theft prevention and child
or family member or home monitoring and other care scenarios. It
supports capture, analysis, recording, and monitoring of video data coming
from one or more video or network cameras attached to a Linux system.
ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
cameras using a variety of protocols. It is suitable for use as a home
video security system and for commercial or professional video security
and surveillance. It can also be integrated into a home automation system
via X.10 or other protocols.
Package: zoneminder-dbg
Section: debug
Priority: extra
Architecture: any
Depends: zoneminder (= ${binary:Version}), ${misc:Depends}
Description: Debugging symbols for zoneminder.
ZoneMinder is a video camera security and surveillance solution.
ZoneMinder is intended for use in single or multi-camera video security
applications, including commercial or home CCTV, theft prevention and child
or family member or home monitoring and other care scenarios. It
supports capture, analysis, recording, and monitoring of video data coming
from one or more video or network cameras attached to a Linux system.
ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
cameras using a variety of protocols. It is suitable for use as a home
video security system and for commercial or professional video security
and surveillance. It can also be integrated into a home automation system
via X.10 or other protocols.

View File

@ -0,0 +1,22 @@
Copyright:
Copyright 2002 Philip Coombes <philip.coombes@zoneminder.com>
License:
This package 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 package 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 package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
On Debian GNU/Linux systems, the text of the GPL can be found in
/usr/share/common-licenses/GPL.

6
distros/ubuntu1504/dirs Normal file
View File

@ -0,0 +1,6 @@
var/log/zm
var/lib/zm
var/cache/zoneminder/events
var/cache/zoneminder/images
var/cache/zoneminder/temp
usr/share/zoneminder/db

1
distros/ubuntu1504/docs Normal file
View File

@ -0,0 +1 @@
README.md

View File

@ -0,0 +1,12 @@
usr/bin
usr/lib/cgi-bin
usr/share/man
usr/share/perl5/ZoneMinder
usr/share/perl5/ZoneMinder.pm
usr/share/polkit-1/actions
usr/share/polkit-1/rules.d
usr/share/zoneminder
etc/zm
db/zm_create.sql usr/share/zoneminder/db
db/zm_update-*.sql usr/share/zoneminder/db
debian/apache.conf etc/zm

4
distros/ubuntu1504/links Normal file
View File

@ -0,0 +1,4 @@
var/cache/zoneminder/events usr/share/zoneminder/events
var/cache/zoneminder/images usr/share/zoneminder/images
var/cache/zoneminder/temp usr/share/zoneminder/temp
usr/lib/cgi-bin usr/share/zoneminder/cgi-bin

View File

View File

@ -0,0 +1,53 @@
#! /bin/sh
set -e
if [ "$1" = "configure" ]; then
if [ -e "/lib/systemd/system/mysql.service" ]; then
#
# Get mysql started if it isn't
#
if ! $(systemctl is-active mysql >/dev/null 2>&1); then
systemctl start mysql
fi
if $(systemctl is-active mysql >/dev/null 2>&1); then
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
# test if database if already present...
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi
systemctl stop zoneminder || true #not sure about "|| true"
zmupdate.pl --nointeractive
else
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi
else
echo 'mysql not found, assuming remote server.'
fi
chown www-data:www-data /var/log/zm
chown www-data:www-data /var/lib/zm/
if [ -z "$2" ]; then
chown www-data:www-data -R /var/cache/zoneminder
fi
fi
# Ensure zoneminder is stopped...
if [ -e "/lib/systemd/system/zoneminder.service" ]; then #changed from -x to -e, could be wrong
if systemctl is-active zoneminder >/dev/null 2>&1 ; then
systemctl stop zoneminder || exit $?
fi
fi
if [ "$1" = "configure" ]; then
if [ -z "$2" ]; then
chown www-data:www-data /var/log/zm
chown www-data:www-data /var/lib/zm/
chown www-data:www-data -R /var/cache/zoneminder
else
chown www-data:www-data /var/log/zm
zmupdate.pl
fi
fi
#DEBHELPER#

View File

@ -0,0 +1,9 @@
#! /bin/sh
# set -e # to be reinstated later
if [ "$1" = "purge" ]; then
echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm
fi
#DEBHELPER#

32
distros/ubuntu1504/preinst Executable file
View File

@ -0,0 +1,32 @@
#!/bin/sh
set -e
abort=false
if [ -L /usr/share/zoneminder/events ]; then
l=$(readlink /usr/share/zoneminder/events)
if [ "$l" != "/var/cache/zoneminder/events" ]; then
abort=true
fi
fi
if [ -L /usr/share/zoneminder/images ]; then
l=$(readlink /usr/share/zoneminder/images )
if [ "$l" != "/var/cache/zoneminder/images" ]; then
abort=true
fi
fi
if [ "$abort" = "true" ]; then
cat >&2 << EOF
Aborting installation of zoneminder due to non-default symlinks in
/usr/share/zoneminder for the images and/or events directory, which could
result in loss of data. Please move your data in each of these directories to
/var/cache/zoneminder before installing zoneminder from the package.
EOF
exit 1
fi
#DEBHELPER#
exit 0

67
distros/ubuntu1504/rules Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
CFLAGS = -Wall -g
CPPFLAGS = -D__STDC_CONSTANT_MACROS
CXXFLAGS = -DHAVE_LIBCRYPTO
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS += -O0
else
CFLAGS += -O2
endif
%:
dh $@ --with quilt,autoreconf,systemd
override_dh_auto_configure:
CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --with-mysql=/usr --with-mariadb=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes
override_dh_clean:
# Add here commands to clean up after the build process.
[ ! -f Makefile ] || $(MAKE) distclean
dh_clean
override_dh_install:
dh_install --fail-missing
#
# NOTE: This is a short-term kludge; hopefully changes in the next
# upstream version will render this unnecessary.
rm -rf debian/zoneminder/usr/share/zoneminder/events
rm -rf debian/zoneminder/usr/share/zoneminder/images
rm -rf debian/zoneminder/usr/share/zoneminder/temp
dh_link var/cache/zoneminder/events usr/share/zoneminder/events
dh_link var/cache/zoneminder/images usr/share/zoneminder/images
dh_link var/cache/zoneminder/temp usr/share/zoneminder/temp
#
# This is a slightly lesser kludge; moving the cgi stuff to
# /usr/share/zoneminder/cgi-bin breaks one set of behavior,
# having it just in /usr/lib/cgi-bin breaks another bit of
# behavior.
#
dh_link usr/lib/cgi-bin usr/share/zoneminder/cgi-bin
override_dh_fixperms:
dh_fixperms
chown root:root debian/zoneminder/etc/zm/zm.conf
override_dh_auto_test:
# do not run tests...
.PHONY: override_dh_strip
override_dh_strip:
dh_strip --dbg-package=zoneminder-dbg

3
distros/ubuntu1504/watch Normal file
View File

@ -0,0 +1,3 @@
version=3
http://www.zoneminder.com/downloads.html \
.*/ZoneMinder-(.*).tar.gz

View File

@ -0,0 +1,16 @@
# ZoneMinder systemd unit file
# This file is intended to work with all Linux distributions
[Unit]
Description=ZoneMinder CCTV recording and security system
After=network.target mysql.service apache2.service
Requires=mysql.service apache2.service
[Service]
User=www-data
Type=forking
ExecStart=/usr/bin/zmpkg.pl start
ExecReload=/usr/bin/zmpkg.pl restart
ExecStop=/usr/bin/zmpkg.pl stop
PIDFile="/run/zm/zm.pid"
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,2 @@
d /var/run/zm 0755 www-data www-data

63
m4/ac_check_sendfile.m4 Normal file
View File

@ -0,0 +1,63 @@
AC_DEFUN([AC_CHECK_SENDFILE],[
AC_MSG_CHECKING([whether sendfile() is supported and what prototype it has])
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Werror-implicit-function-declaration"
ac_sendfile_supported=no
AC_TRY_LINK([#include <sys/sendfile.h>
#include <stdio.h>],
[sendfile(1, 1, NULL, 0);],
[
AC_DEFINE(HAVE_SENDFILE4_SUPPORT, 1,
[Define this if Linux/Solaris sendfile() is supported])
AC_MSG_RESULT([Linux sendfile()])
ac_sendfile_supported=yes
], [])
if test x$ac_sendfile_supported = xno; then
dnl Checking wether we need libsendfile
dnl Presumably on Solaris
AC_CHECK_LIB(sendfile, sendfile,
[
AC_DEFINE(HAVE_SENDFILE4_SUPPORT, 1,
[Define this if Linux/Solaris sendfile() is supported])
SENDFILE_LIBS="-lsendfile"
AC_SUBST(SENDFILE_LIBS)
AC_MSG_RESULT([Solaris sendfile()])
ac_sendfile_supported=yes
], [])
fi
if test x$ac_sendfile_supported = xno; then
dnl Checking wether we have FreeBSD-like sendfile() support.
AC_TRY_LINK([#include <sys/socket.h>
#include <stdio.h>],
[sendfile(1, 1, 0, 0, NULL, NULL, 0);],
[
AC_DEFINE(HAVE_SENDFILE7_SUPPORT, 1,
[Define this if FreeBSD sendfile() is supported])
AC_MSG_RESULT([FreeBSD sendfile()])
ac_sendfile_supported=yes
], [])
fi
if test x$ac_sendfile_supported = xno; then
dnl Checking wether we have MacOS-like sendfile() support.
AC_TRY_LINK([#include <sys/socket.h>
#include <stdio.h>
#include <sys/uio.h>],
[sendfile(1, 1, 0, NULL, NULL, 0);],
[
AC_DEFINE(HAVE_SENDFILE6_SUPPORT, 1,
[Define this if MacOS sendfile() is supported])
AC_MSG_RESULT([MacOS sendfile()])
ac_sendfile_supported=yes
], [])
fi
CFLAGS="$saved_CFLAGS"
if test x$ac_sendfile_supported = xno; then
AC_MSG_RESULT([no sendfile() support, using read/send])
fi
])

View File

@ -1 +1,4 @@
d @ZM_RUNDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_RUNDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@

View File

@ -1,10 +1,18 @@
# CMakeLists.txt for the ZoneMinder ONVIF modules.
# If this is an out-of-source build, copy the files we need to the binary directory
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
# MAKEMAKER_NOECHO_COMMAND previously defined in /scripts/zoneminder/CMakeLists.txt
# Add build target for the perl modules
add_custom_target(zmonvifmodules ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules")
add_custom_target(zmonvifmodules ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl ONVIF proxy module")
# Add install target for the perl modules
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/")
# Add additional files and directories to make clean
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl")

View File

@ -1,10 +1,18 @@
# CMakeLists.txt for the ZoneMinder ONVIF proxy module.
# If this is an out-of-source build, copy the files we need to the binary directory
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
# MAKEMAKER_NOECHO_COMMAND previously defined in /scripts/zoneminder/CMakeLists.txt
# Add build target for the perl modules
add_custom_target(zmonvifproxy ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules")
add_custom_target(zmonvifproxy ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl ONVIF proxy module")
# Add install target for the perl modules
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/")
# Add additional files and directories to make clean
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl")

View File

@ -2,13 +2,13 @@
# If this is an out-of-source build, copy the files we need to the binary directory
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Changes" "${CMAKE_CURRENT_BINARY_DIR}/Changes")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.PL")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST" "${CMAKE_CURRENT_BINARY_DIR}/MANIFEST")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/META.yml" "${CMAKE_CURRENT_BINARY_DIR}/META.yml")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/README" "${CMAKE_CURRENT_BINARY_DIR}/README")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/t" "${CMAKE_CURRENT_BINARY_DIR}/t")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/lib" "${CMAKE_CURRENT_BINARY_DIR}/lib")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Changes" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/META.yml" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/README" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/t" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
# Create files from the .in files
@ -24,10 +24,10 @@ else(CMAKE_VERBOSE_MAKEFILE)
endif(CMAKE_VERBOSE_MAKEFILE)
# Add build target for the perl modules
add_custom_target(zmperlmodules ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules")
add_custom_target(zmperlmodules ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules")
# Add install target for the perl modules
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/")
# Add additional files and directories to make clean
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl")

View File

@ -36,34 +36,42 @@ use ZoneMinder::General qw(:all);
use ZoneMinder::Database 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
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'base' => [
@ZoneMinder::Base::EXPORT_OK
],
'config' => [
@ZoneMinder::Config::EXPORT_OK
],
'debug' => [
@ZoneMinder::Logger::EXPORT_OK
],
'general' => [
@ZoneMinder::General::EXPORT_OK
],
'database' => [
@ZoneMinder::Database::EXPORT_OK
],
'memory' => [
@ZoneMinder::Memory::EXPORT_OK
],
'base' => [
@ZoneMinder::Base::EXPORT_OK
],
'config' => [
@ZoneMinder::Config::EXPORT_OK
],
'debug' => [
@ZoneMinder::Logger::EXPORT_OK
],
'general' => [
@ZoneMinder::General::EXPORT_OK
],
'database' => [
@ZoneMinder::Database::EXPORT_OK
],
'memory' => [
@ZoneMinder::Memory::EXPORT_OK
],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Base;
@ -38,7 +38,7 @@ use constant ZM_VERSION => "@VERSION@";
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(ZM_VERSION) ] );
@ -62,7 +62,11 @@ ZoneMinder::Base - Base perl module for ZoneMinder
=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

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Config;
@ -38,15 +38,15 @@ use vars qw( %Config );
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our @EXPORT_CONFIG = qw( %Config ); # Get populated by BEGIN
our %EXPORT_TAGS = (
'constants' => [ qw(
ZM_PID
) ]
'constants' => [ qw(
ZM_PID
) ]
);
push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG );
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -65,38 +65,43 @@ use Carp;
# Load the config from the database into the symbol table
BEGIN
{
my $config_file = ZM_CONFIG;
( my $local_config_file = $config_file ) =~ s|^.*/|./|;
if ( -s $local_config_file and -r $local_config_file )
{
print( STDERR "Warning, overriding installed $local_config_file file with local copy\n" );
$config_file = $local_config_file;
}
open( CONFIG, "<".$config_file ) or croak( "Can't open config file '$config_file': $!" );
foreach my $str ( <CONFIG> )
{
next if ( $str =~ /^\s*$/ );
next if ( $str =~ /^\s*#/ );
my $config_file = ZM_CONFIG;
( my $local_config_file = $config_file ) =~ s|^.*/|./|;
if ( -s $local_config_file and -r $local_config_file )
{
print( STDERR "Warning, overriding installed $local_config_file file with local copy\n" );
$config_file = $local_config_file;
}
open( my $CONFIG, "<", $config_file )
or croak( "Can't open config file '$config_file': $!" );
foreach my $str ( <$CONFIG> )
{
next if ( $str =~ /^\s*$/ );
next if ( $str =~ /^\s*#/ );
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/;
if ( ! $name ) {
print( STDERR "Warning, bad line in $config_file: $str\n" );
next;
} # end if
$name =~ tr/a-z/A-Z/;
$Config{$name} = $value;
}
close( CONFIG );
if ( ! $name ) {
print( STDERR "Warning, bad line in $config_file: $str\n" );
next;
} # end if
$name =~ tr/a-z/A-Z/;
$Config{$name} = $value;
}
close( $CONFIG );
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 $sql = 'select * from Config';
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() );
while( my $config = $sth->fetchrow_hashref() ) {
$Config{$config->{Name}} = $config->{Value};
}
$sth->finish();
#$dbh->disconnect();
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 $sql = 'select * from Config';
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() );
while( my $config = $sth->fetchrow_hashref() ) {
$Config{$config->{Name}} = $config->{Value};
}
$sth->finish();
#$dbh->disconnect();
}
1;
@ -112,9 +117,16 @@ ZoneMinder::Config - ZoneMinder configuration module.
=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};

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the debug definitions and functions used by the rest
# This module contains the debug definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::ConfigAdmin;
@ -37,14 +37,14 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
loadConfigFromDB
saveConfigToDB
) ]
'functions' => [ qw(
loadConfigFromDB
saveConfigToDB
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -67,99 +67,138 @@ use Carp;
sub loadConfigFromDB
{
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} );
if ( !$dbh )
{
print( "Error: unable to load options from database: $DBI::errstr\n" );
return( 0 );
}
my $sql = "select * from Config";
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 $option_count = 0;
while( my $config = $sth->fetchrow_hashref() )
{
my ( $name, $value ) = ( $config->{Name}, $config->{Value} );
#print( "Name = '$name'\n" );
my $option = $options_hash{$name};
if ( !$option )
{
warn( "No option '$name' found, removing" );
next;
}
#next if ( $option->{category} eq 'hidden' );
if ( defined($value) )
{
if ( $option->{type} == $types{boolean} )
{
$option->{value} = $value?"yes":"no";
}
else
{
$option->{value} = $value;
}
}
$option_count++;;
}
$sth->finish();
$dbh->disconnect();
return( $option_count );
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}
);
if ( !$dbh )
{
print( "Error: unable to load options from database: $DBI::errstr\n" );
return( 0 );
}
my $sql = "select * from Config";
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 $option_count = 0;
while( my $config = $sth->fetchrow_hashref() )
{
my ( $name, $value ) = ( $config->{Name}, $config->{Value} );
#print( "Name = '$name'\n" );
my $option = $options_hash{$name};
if ( !$option )
{
warn( "No option '$name' found, removing" );
next;
}
#next if ( $option->{category} eq 'hidden' );
if ( defined($value) )
{
if ( $option->{type} == $types{boolean} )
{
$option->{value} = $value?"yes":"no";
}
else
{
$option->{value} = $value;
}
}
$option_count++;;
}
$sth->finish();
$dbh->disconnect();
return( $option_count );
}
sub saveConfigToDB
{
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} );
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}
);
if ( !$dbh )
{
print( "Error: unable to save options to database: $DBI::errstr\n" );
return( 0 );
}
if ( !$dbh )
{
print( "Error: unable to save options to database: $DBI::errstr\n" );
return( 0 );
}
my $ac = $dbh->{AutoCommit};
$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 $res = $dbh->do( $sql ) or croak( "Can't do '$sql': ".$dbh->errstr() );
my $sql = "delete from Config";
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 = ?";
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
foreach my $option ( @options )
{
#next if ( $option->{category} eq 'hidden' );
#print( $option->{name}."\n" ) if ( !$option->{category} );
$option->{db_type} = $option->{type}->{db_type};
$option->{db_hint} = $option->{type}->{hint};
$option->{db_pattern} = $option->{type}->{pattern};
$option->{db_format} = $option->{type}->{format};
if ( $option->{db_type} eq "boolean" )
{
$option->{db_value} = ($option->{value} eq "yes")?"1":"0";
}
else
{
$option->{db_value} = $option->{value};
}
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 );
}
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() );
}
$sth->finish();
$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() );
foreach my $option ( @options )
{
#next if ( $option->{category} eq 'hidden' );
#print( $option->{name}."\n" ) if ( !$option->{category} );
$option->{db_type} = $option->{type}->{db_type};
$option->{db_hint} = $option->{type}->{hint};
$option->{db_pattern} = $option->{type}->{pattern};
$option->{db_format} = $option->{type}->{format};
if ( $option->{db_type} eq "boolean" )
{
$option->{db_value} = ($option->{value} eq "yes")
? "1"
: "0"
;
}
else
{
$option->{db_value} = $option->{value};
}
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
);
}
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() );
}
$sth->finish();
$dbh->do('UNLOCK TABLES');
$dbh->{AutoCommit} = $ac;
$dbh->disconnect();
$dbh->disconnect();
}
1;
@ -179,9 +218,15 @@ ZoneMinder::ConfigAdmin - ZoneMinder Configuration Administration module
=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
@ -189,11 +234,19 @@ The configuration held in this module, which was previously in zmconfig.pl, incl
=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 ();
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
=head2 EXPORT

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the base class definitions for the camera control
# This module contains the base class definitions for the camera control
# protocol implementations
#
package ZoneMinder::Control;
@ -45,17 +45,17 @@ our $AUTOLOAD;
sub new
{
my $class = shift;
my $id = shift;
my $self = {};
$self->{name} = "PelcoD";
my $class = shift;
my $id = shift;
my $self = {};
$self->{name} = "PelcoD";
if ( !defined($id) )
{
Fatal( "No monitor defined when invoking protocol ".$self->{name} );
}
$self->{id} = $id;
bless( $self, $class );
return $self;
$self->{id} = $id;
bless( $self, $class );
return $self;
}
sub DESTROY
@ -64,32 +64,32 @@ sub DESTROY
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
croak( "Can't access $name member of object of class $class" );
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
croak( "Can't access $name member of object of class $class" );
}
sub getKey()
sub getKey
{
my $self = shift;
my $self = shift;
return( $self->{id} );
}
sub open
{
my $self = shift;
my $self = shift;
Fatal( "No open method defined for protocol ".$self->{name} );
}
sub close
{
my $self = shift;
my $self = shift;
Fatal( "No close method defined for protocol ".$self->{name} );
}
@ -145,9 +145,9 @@ sub executeCommand
&{$self->{$command}}( $self, $params );
}
sub printMsg()
sub printMsg
{
my $self = shift;
my $self = shift;
Fatal( "No printMsg method defined for protocol ".$self->{name} );
}

View File

@ -21,10 +21,10 @@
#
# This module contains the implementation of the 3S camera control
# protocol
#Model: N5071
#Hardware Version: 00
#Firmware Version: V1.03_STD-1
#Firmware Build Time: Jun 19 2012 15:28:17
#Model: N5071
#Hardware Version: 00
#Firmware Version: V1.03_STD-1
#Firmware Build Time: Jun 19 2012 15:28:17
package ZoneMinder::Control::3S;

View File

@ -53,7 +53,7 @@ our @ISA = qw(ZoneMinder::Control);
# of the position of your mouse on the arrow.
# Extremity of arrow equal to fastest 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
# -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
@ -63,7 +63,7 @@ our @ISA = qw(ZoneMinder::Control);
# -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 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)
# -"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
@ -129,7 +129,7 @@ sub printMsg
}
sub sendCmd
{
{
my $self = shift;
my $cmd = shift;
my $result = undef;
@ -211,7 +211,7 @@ sub moveConUp
if ( $tiltspeed < 10 ) {
$tiltspeed = 1;
}
Debug( "Move Up" );
Debug( "Move Up" );
if ( $osd eq "on" )
{
my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Up $tiltspeed";
@ -658,9 +658,9 @@ sub presetGoto
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 FI-8608W
=head1 NAME
ZoneMinder::Database - Perl extension for FOSCAM FI-8608W by Christophe_Y2k
ZoneMinder::Control::FI-8608W - Perl extension for FOSCAM FI-8608W by Christophe_Y2k
=head1 SYNOPSIS

View File

@ -57,7 +57,7 @@ our @ISA = qw(ZoneMinder::Control);
# of the position of your mouse on the arrow.
# Extremity of arrow equal to fastest 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
# -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)
@ -67,7 +67,7 @@ our @ISA = qw(ZoneMinder::Control);
# 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 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...
# (you need to click to the center arrow for stop)
# -"White In" to control Brightness, "auto" for restore the original value of Brightness
@ -734,9 +734,9 @@ sub presetGoto
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 FI8620
=head1 NAME
ZoneMinder::Database - Perl extension for FOSCAM FI8620
ZoneMinder::Control::FI8620 - Perl extension for FOSCAM FI8620
=head1 SYNOPSIS

View File

@ -43,176 +43,176 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
my $self = shift;
$self->loadMonitor();
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $result = undef;
my $self = shift;
my $cmd = shift;
my $result = undef;
my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice};
my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice};
if ( !defined $password ) {
# If value of "Control device" does not consist of two parts, then only password is given and we fallback to default user:
$password = $user;
$user = 'admin';
}
if ( !defined $password ) {
# If value of "Control device" does not consist of two parts, then only password is given and we fallback to default user:
$password = $user;
$user = 'admin';
}
$cmd .= "user=$user&pwd=$password";
$cmd .= "user=$user&pwd=$password";
printMsg( $cmd, "Tx" );
printMsg( $cmd, "Tx" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
my $res = $self->{ua}->request($req);
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed: '".$res->status_line()."' for URL ".$req->uri() );
}
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed: '".$res->status_line()."' for URL ".$req->uri() );
}
return( $result );
return( $result );
}
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
$self->sendCmd( 'reboot.cgi?' );
my $self = shift;
Debug( "Camera Reset" );
$self->sendCmd( 'reboot.cgi?' );
}
#Up Arrow
sub moveConUp
{
my $self = shift;
Debug( "Move Up" );
$self->sendCmd( 'decoder_control.cgi?command=0&' );
my $self = shift;
Debug( "Move Up" );
$self->sendCmd( 'decoder_control.cgi?command=0&' );
}
#Down Arrow
sub moveConDown
{
my $self = shift;
Debug( "Move Down" );
$self->sendCmd( 'decoder_control.cgi?command=2&' );
my $self = shift;
Debug( "Move Down" );
$self->sendCmd( 'decoder_control.cgi?command=2&' );
}
#Left Arrow
sub moveConLeft
{
my $self = shift;
Debug( "Move Left" );
$self->sendCmd( 'decoder_control.cgi?command=6&' );
my $self = shift;
Debug( "Move Left" );
$self->sendCmd( 'decoder_control.cgi?command=6&' );
}
#Right Arrow
sub moveConRight
{
my $self = shift;
Debug( "Move Right" );
$self->sendCmd( 'decoder_control.cgi?command=4&' );
my $self = shift;
Debug( "Move Right" );
$self->sendCmd( 'decoder_control.cgi?command=4&' );
}
#Diagonally Up Right Arrow
sub moveConUpRight
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
$self->sendCmd( 'decoder_control.cgi?command=90&' );
my $self = shift;
Debug( "Move Diagonally Up Right" );
$self->sendCmd( 'decoder_control.cgi?command=90&' );
}
#Diagonally Down Right Arrow
sub moveConDownRight
{
my $self = shift;
Debug( "Move Diagonally Down Right" );
$self->sendCmd( 'decoder_control.cgi?command=92&' );
my $self = shift;
Debug( "Move Diagonally Down Right" );
$self->sendCmd( 'decoder_control.cgi?command=92&' );
}
#Diagonally Up Left Arrow
sub moveConUpLeft
{
my $self = shift;
Debug( "Move Diagonally Up Left" );
$self->sendCmd( 'decoder_control.cgi?command=91&' );
my $self = shift;
Debug( "Move Diagonally Up Left" );
$self->sendCmd( 'decoder_control.cgi?command=91&' );
}
#Diagonally Down Left Arrow
sub moveConDownLeft
{
my $self = shift;
Debug( "Move Diagonally Down Left" );
$self->sendCmd( 'decoder_control.cgi?command=93&' );
my $self = shift;
Debug( "Move Diagonally Down Left" );
$self->sendCmd( 'decoder_control.cgi?command=93&' );
}
#Stop
sub moveStop
{
my $self = shift;
Debug( "Move Stop" );
$self->sendCmd( 'decoder_control.cgi?command=1&' );
my $self = shift;
Debug( "Move Stop" );
$self->sendCmd( 'decoder_control.cgi?command=1&' );
}
#Move Camera to Home Position
sub presetHome
{
my $self = shift;
Debug( "Home Preset" );
$self->sendCmd( 'decoder_control.cgi?command=25&' );
my $self = shift;
Debug( "Home Preset" );
$self->sendCmd( 'decoder_control.cgi?command=25&' );
}
1;
@ -220,12 +220,17 @@ sub presetHome
__END__
=pod
=head1 NAME
ZoneMinder::Control::FI8908W - Foscam FI8908W camera control
=head1 DESCRIPTION
This module contains the implementation of the Foscam FI8908W / FI8918W IP camera control
protocol.
This module contains the implementation of the Foscam FI8908W / FI8918W IP
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

View File

@ -102,7 +102,6 @@ sub close
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
@ -113,9 +112,28 @@ sub sendCmd
my $self = shift;
my $cmd = shift;
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" );
my $temps = time();
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 );
my $url;
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);
if ( $res->is_success )
{
@ -134,7 +152,7 @@ sub reset
# Setup OSD
my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0";
$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";
$self->sendCmd( $cmd );
# Setup For Infrared AUTO
@ -696,9 +714,9 @@ sub presetGoto
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 FI9821W
=head1 NAME
ZoneMinder::Database - Perl extension for FOSCAM FI9821W
ZoneMinder::Control::FI9821W - Perl extension for FOSCAM FI9821W
=head1 SYNOPSIS

View File

@ -107,14 +107,18 @@ sub sendCmd {
printMsg( $cmd, "Tx" );
#print( "http://$address/$cmd\n" );
#my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
my $url;
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time;
} else {
$url = 'http://'.$self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time;
} # en dif
Error("Url: $url $cmd");
my $url;
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress}
.'/cgi-bin/setGPIO.cgi?preventCache='.time
;
} else {
$url = 'http://'.$self->{Monitor}->{ControlAddress}
.'/cgi-bin/setGPIO.cgi?preventCache='.time
;
} # en dif
Error("Url: $url $cmd");
my $uri = URI::Encode->new( { encode_reserved => 0 } );
my $encoded = $uri->encode( $cmd );
my $res = $self->{ua}->post( $url, Content=>"data=$encoded" );
@ -203,7 +207,11 @@ sub moveMap
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, '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 );
}

View File

@ -22,7 +22,7 @@
# This module contains the implementation of the Pelco-P camera control
# protocol
#
package ZoneMinder::Control::PelcoD;
package ZoneMinder::Control::PelcoP;
use 5.006;
use strict;

View File

@ -60,16 +60,16 @@ our @ISA = qw(ZoneMinder::Control);
#
# ******** YOU MUST CHANGE THE FOLLOWING LINES TO MATCH YOUR CAMERA! **********
#
#
# I assume that "TV-IP672WI" would work for the TV-IP672WI, but can't test since I don't own one.
#
#
# TV-IP672PI works for the PI version, of course.
#
# Finally, the username is the username you'd like to authenticate as.
#
our $REALM = 'TV-IP862IC';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $PASSWORD = '';
our $ADDRESS = '';
# ==========================================================================
@ -111,26 +111,32 @@ sub open
my $self = shift;
$self->loadMonitor();
my ( $protocol, $username, $password, $address ) = $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/;
if ( $username ) {
$USERNAME = $username;
$PASSWORD = $password;
$ADDRESS = $address;
} else {
Error( "Failed to parse auth from address");
$ADDRESS = $self->{Monitor}->{ControlAddress};
}
if ( ! $ADDRESS =~ /:/ ) {
Error( "You generally need to also specify the port. I will append :80" );
$ADDRESS .= ':80';
}
my ( $protocol, $username, $password, $address )
= $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/;
if ( $username ) {
$USERNAME = $username;
$PASSWORD = $password;
$ADDRESS = $address;
} else {
Error( "Failed to parse auth from address");
$ADDRESS = $self->{Monitor}->{ControlAddress};
}
if ( ! $ADDRESS =~ /:/ ) {
Error( "You generally need to also specify the port. I will append :80" );
$ADDRESS .= ':80';
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug ( "sendCmd credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug ( "sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
}
@ -159,29 +165,29 @@ sub sendCmd
my $result = undef;
my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd;
my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd;
my $req = HTTP::Request->new( GET=>$url );
Debug ("sendCmd command: " . $url );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
}
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
}
}
return( $result );
@ -203,10 +209,10 @@ sub sendCmdPost
my $result = undef;
if ($url eq undef)
{
Error ("url passed to sendCmdPost is undefined.");
return(-1);
}
{
Error ("url passed to sendCmdPost is undefined.");
return(-1);
}
Debug ("sendCmdPost url: " . $url . " cmd: " . $cmd);
@ -215,7 +221,7 @@ sub sendCmdPost
$req->content($cmd);
Debug ( "sendCmdPost credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
my $res = $self->{ua}->request($req);
if ( $res->is_success )
@ -225,11 +231,11 @@ sub sendCmdPost
else
{
Error( "sendCmdPost Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} else {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} # endif
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} else {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} # endif
}
return( $result );

View File

@ -16,8 +16,8 @@
# Rename to Wanscam
# Pan Left/Right switched
# IR On/Off switched
# Brightness Increase/Decrease in 16 steps
#
# Brightness Increase/Decrease in 16 steps
#
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License

View File

@ -126,32 +126,32 @@ sub sendCmd
sub Up
{
my $self = shift;
$self->moveConUp();
$self->moveConUp();
}
sub Down
{
my $self = shift;
$self->moveConDown();
$self->moveConDown();
}
sub Left
{
my $self = shift;
$self->moveConLeft();
$self->moveConLeft();
}
sub Right
{
my $self = shift;
$self->moveConRight();
$self->moveConRight();
}
sub reset
{
my $self = shift;
$self->cameraReset();
$self->cameraReset();
}

View File

@ -36,7 +36,7 @@ our %CamParams = ();
# ==========================================================================
#
# ONVIF Control Protocol
#
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:@10.1.2.1:80
@ -50,7 +50,7 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
{
my $class = shift;
my $id = shift;
@ -90,7 +90,7 @@ sub open
}
sub close
{
{
my $self = shift;
$self->{state} = 'closed';
}
@ -133,7 +133,7 @@ sub getCamParams
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/get_camera_params.cgi" );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
if ( $res->is_success )
{
# Parse results setting values in %FCParams
my $content = $res->decoded_content;
@ -141,7 +141,7 @@ sub getCamParams
while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) {
$CamParams{$1} = $2;
}
}
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Database;
@ -37,17 +37,17 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
zmDbConnect
zmDbDisconnect
zmDbGetMonitors
zmDbGetMonitor
zmDbGetMonitorAndControl
) ]
zmDbConnect
zmDbDisconnect
zmDbGetMonitors
zmDbGetMonitor
zmDbGetMonitorAndControl
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -70,37 +70,46 @@ use Carp;
our $dbh = undef;
sub zmDbConnect( ;$ )
sub zmDbConnect
{
my $force = shift;
if ( $force )
{
zmDbDisconnect();
}
if ( !defined( $dbh ) )
{
my $force = shift;
if ( $force )
{
zmDbDisconnect();
}
if ( !defined( $dbh ) )
{
my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
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
{
$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 );
}
return( $dbh );
}
return( $dbh );
}
sub zmDbDisconnect()
sub zmDbDisconnect
{
if ( defined( $dbh ) )
{
$dbh->disconnect();
$dbh = undef;
}
if ( defined( $dbh ) )
{
$dbh->disconnect();
$dbh = undef;
}
}
use constant DB_MON_ALL => 0; # All monitors
@ -110,78 +119,88 @@ use constant DB_MON_MOTION => 3; # All monitors that are doing motion detection
use constant DB_MON_RECORD => 4; # All monitors that are doing unconditional recording
use constant DB_MON_PASSIVE => 5; # All monitors that are in nodect state
sub zmDbGetMonitors( ;$ )
sub zmDbGetMonitors
{
zmDbConnect();
zmDbConnect();
my $function = shift || DB_MON_ALL;
my $sql = "select * from Monitors";
my $function = shift || DB_MON_ALL;
my $sql = "select * from Monitors";
if ( $function )
{
if ( $function == DB_MON_CAPT )
{
$sql .= " where Function >= 'Monitor'";
}
elsif ( $function == DB_MON_ACTIVE )
{
$sql .= " where Function > 'Monitor'";
}
elsif ( $function == DB_MON_MOTION )
{
$sql .= " where Function = 'Modect' or Function = 'Mocord'";
}
elsif ( $function == DB_MON_RECORD )
{
$sql .= " where Function = 'Record' or Function = 'Mocord'";
}
elsif ( $function == DB_MON_PASSIVE )
{
$sql .= " where Function = 'Nodect'";
}
}
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or croak( "Can't execute '$sql': ".$sth->errstr() );
if ( $function )
{
if ( $function == DB_MON_CAPT )
{
$sql .= " where Function >= 'Monitor'";
}
elsif ( $function == DB_MON_ACTIVE )
{
$sql .= " where Function > 'Monitor'";
}
elsif ( $function == DB_MON_MOTION )
{
$sql .= " where Function = 'Modect' or Function = 'Mocord'";
}
elsif ( $function == DB_MON_RECORD )
{
$sql .= " where Function = 'Record' or Function = 'Mocord'";
}
elsif ( $function == DB_MON_PASSIVE )
{
$sql .= " where Function = 'Nodect'";
}
}
my $sth = $dbh->prepare_cached( $sql )
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() )
{
push( @monitors, $monitor );
}
$sth->finish();
return( \@monitors );
push( @monitors, $monitor );
}
$sth->finish();
return( \@monitors );
}
sub zmDbGetMonitor( $ )
sub zmDbGetMonitor
{
zmDbConnect();
zmDbConnect();
my $id = shift;
my $id = shift;
return( undef ) if ( !defined($id) );
return( undef ) if ( !defined($id) );
my $sql = "select * from Monitors where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $id ) or croak( "Can't execute '$sql': ".$sth->errstr() );
my $sql = "select * from Monitors where Id = ?";
my $sth = $dbh->prepare_cached( $sql )
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();
return( $monitor );
return( $monitor );
}
sub zmDbGetMonitorAndControl( $ )
sub zmDbGetMonitorAndControl
{
zmDbConnect();
zmDbConnect();
my $id = shift;
my $id = shift;
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 $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 $sql = "SELECT C.*,M.*,C.Protocol
FROM Monitors as M
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();
return( $monitor );
return( $monitor );
}
1;

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::General;
@ -76,7 +76,7 @@ use ZoneMinder::Database qw(:all);
use POSIX;
# For running general shell commands
sub executeShellCommand( $ )
sub executeShellCommand
{
my $command = shift;
my $output = qx( $command );
@ -90,7 +90,7 @@ sub executeShellCommand( $ )
return( $status );
}
sub getCmdFormat()
sub getCmdFormat
{
Debug( "Testing valid shell syntax\n" );
@ -162,7 +162,7 @@ our $testedShellSyntax = 0;
our ( $cmdPrefix, $cmdSuffix );
# For running ZM daemons etc
sub runCommand( $ )
sub runCommand
{
if ( !$testedShellSyntax )
{
@ -196,30 +196,45 @@ sub runCommand( $ )
return( $output );
}
sub getEventPath( $ )
sub getEventPath
{
my $event = shift;
my $event_path = "";
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
{
$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 );
}
sub createEventPath( $ )
sub createEventPath
{
#
# WARNING assumes running from events directory
#
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};
if ( $Config{ZM_USE_DEEP_STORAGE} )
@ -242,7 +257,8 @@ sub createEventPath( $ )
# Create event id symlink
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 );
$eventPath .= '/'.$timePath;
@ -250,8 +266,9 @@ sub createEventPath( $ )
# Create empty id tag file
$idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" );
close( ID_FP );
open( my $ID_FP, ">", $idFile )
or Fatal( "Can't open $idFile: $!" );
close( $ID_FP );
setFileOwner( $idFile );
}
else
@ -260,8 +277,9 @@ sub createEventPath( $ )
$eventPath .= '/'.$event->{Id};
my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" );
close( ID_FP );
open( my $ID_FP, ">", $idFile )
or Fatal( "Can't open $idFile: $!" );
close( $ID_FP );
setFileOwner( $idFile );
}
return( $eventPath );
@ -272,15 +290,20 @@ use Data::Dumper;
our $_setFileOwner = undef;
our ( $_ownerUid, $_ownerGid );
sub _checkProcessOwner()
sub _checkProcessOwner
{
if ( !defined($_setFileOwner) )
{
my ( $processOwner ) = getpwuid( $> );
if ( $processOwner ne $Config{ZM_WEB_USER} )
{
# Not running as web user, so should be root in whch case chown 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}."': $!" );
# Not running as web user, so should be root in which case chown
# 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;
}
else
@ -291,19 +314,22 @@ sub _checkProcessOwner()
return( $_setFileOwner );
}
sub setFileOwner( $ )
sub setFileOwner
{
my $file = shift;
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}."': $!"
);
}
}
our $_hasImageInfo = undef;
sub _checkForImageInfo()
sub _checkForImageInfo
{
if ( !defined($_hasImageInfo) )
{
@ -317,7 +343,7 @@ sub _checkForImageInfo()
return( $_hasImageInfo );
}
sub createEvent( $;$ )
sub createEvent
{
my $event = shift;
@ -335,9 +361,14 @@ sub createEvent( $;$ )
elsif ( $event->{MonitorId} )
{
my $sql = "select * from Monitors where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
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}."'" );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() );
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();
}
else
@ -358,7 +389,9 @@ sub createEvent( $;$ )
my $imageInfo = Image::Info::image_info( $frame->{imagePath} );
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
{
@ -394,18 +427,25 @@ sub createEvent( $;$ )
push( @values, $event->{$field} );
}
my $sql = "insert into Events (".join(',',@fields).") values (".join(',',@formats).")";
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() );
my $sql = "INSERT INTO Events (".join(',',@fields)
.") VALUES (".join(',',@formats).")"
;
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};
Info( "Created event ".$event->{Id} );
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 $sth = $dbh->prepare_cached( $sql ) 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 $sth = $dbh->prepare_cached( $sql )
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 );
@ -428,26 +468,46 @@ sub createEvent( $;$ )
push( @values, $frame->{$field} );
}
my $sql = "insert into Frames (".join(',',@fields).") values (".join(',',@formats).")";
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() );
my $sql = "insert into Frames (".join(',',@fields)
.") values (".join(',',@formats).")"
;
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};
if ( $frame->{imagePath} )
{
$frame->{capturePath} = sprintf( "%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}.": $!" );
$frame->{capturePath} = sprintf(
"%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} );
if ( 0 && $Config{ZM_CREATE_ANALYSIS_IMAGES} )
{
$frame->{analysePath} = sprintf( "%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}.": $!" );
$frame->{analysePath} = sprintf(
"%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} );
}
}
}
}
sub addEventImage( $$ )
sub addEventImage
{
my $event = shift;
my $frame = shift;
@ -455,7 +515,7 @@ sub addEventImage( $$ )
# TBD
}
sub updateEvent( $ )
sub updateEvent
{
my $event = shift;
@ -467,7 +527,8 @@ sub updateEvent( $ )
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 = (
StartTime => 'from_unixtime(?)',
@ -484,11 +545,13 @@ sub updateEvent( $ )
my $sql = "update Events set ".join(',',@sets)." where Id = ?";
push( @values, $event->{Id} );
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() );
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() );
}
sub deleteEventFiles( $;$ )
sub deleteEventFiles
{
#
# WARNING assumes running from events directory
@ -541,7 +604,7 @@ sub deleteEventFiles( $;$ )
}
}
sub makePath( $;$ )
sub makePath
{
my $path = shift;
my $root = shift;
@ -585,7 +648,7 @@ sub _testJSON
$hasJSONAny = 1 if ( $result );
}
sub _getJSONType( $ )
sub _getJSONType
{
my $value = shift;
return( 'null' ) unless( defined($value) );
@ -596,9 +659,9 @@ sub _getJSONType( $ )
return( 'string' );
}
sub jsonEncode( $ );
sub jsonEncode;
sub jsonEncode( $ )
sub jsonEncode
{
my $value = shift;
@ -649,7 +712,7 @@ sub jsonEncode( $ )
}
}
sub jsonDecode( $ )
sub jsonDecode
{
my $value = shift;

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the debug definitions and functions used by the rest
# This module contains the debug definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Logger;
@ -126,6 +126,7 @@ our %priorities = (
);
our $logger;
our $LOGFILE;
sub new
{
@ -257,7 +258,12 @@ sub initialise( @ )
{
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 )
{
@ -286,11 +292,18 @@ sub initialise( @ )
$this->{autoFlush} = $ENV{'LOG_FLUSH'}?1:0 if ( defined($ENV{'LOG_FLUSH'}) );
$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
{
my $this = shift;
return unless ( $this->{initialised} );
@ -300,7 +313,7 @@ sub terminate()
$this->termLevel( NOLOG );
}
sub reinitialise()
sub reinitialise
{
my $this = shift;
@ -322,7 +335,7 @@ sub reinitialise()
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG );
}
sub limit( $ )
sub limit
{
my $this = shift;
my $level = shift;
@ -331,7 +344,7 @@ sub limit( $ )
return( $level );
}
sub getTargettedEnv( $ )
sub getTargettedEnv
{
my $this = shift;
my $name = shift;
@ -354,7 +367,7 @@ sub getTargettedEnv( $ )
return( $value );
}
sub fetch()
sub fetch
{
if ( !$logger )
{
@ -364,7 +377,7 @@ sub fetch()
return( $logger );
}
sub id( ;$ )
sub id
{
my $this = shift;
my $id = shift;
@ -388,7 +401,7 @@ sub id( ;$ )
return( $this->{id} );
}
sub level( ;$ )
sub level
{
my $this = shift;
my $level = shift;
@ -405,20 +418,20 @@ sub level( ;$ )
return( $this->{level} );
}
sub debugOn()
sub debugOn
{
my $this = shift;
return( $this->{effectiveLevel} >= DEBUG );
}
sub trace( ;$ )
sub trace
{
my $this = shift;
$this->{trace} = $_[0] if ( @_ );
return( $this->{trace} );
}
sub termLevel( ;$ )
sub termLevel
{
my $this = shift;
my $termLevel = shift;
@ -434,7 +447,7 @@ sub termLevel( ;$ )
return( $this->{termLevel} );
}
sub databaseLevel( ;$ )
sub databaseLevel
{
my $this = shift;
my $databaseLevel = shift;
@ -451,23 +464,39 @@ sub databaseLevel( ;$ )
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
{
$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} )
{
$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
{
$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;
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 );
}
}
@ -486,7 +515,7 @@ sub databaseLevel( ;$ )
return( $this->{databaseLevel} );
}
sub fileLevel( ;$ )
sub fileLevel
{
my $this = shift;
my $fileLevel = shift;
@ -503,7 +532,7 @@ sub fileLevel( ;$ )
return( $this->{fileLevel} );
}
sub syslogLevel( ;$ )
sub syslogLevel
{
my $this = shift;
my $syslogLevel = shift;
@ -520,19 +549,19 @@ sub syslogLevel( ;$ )
return( $this->{syslogLevel} );
}
sub openSyslog()
sub openSyslog
{
my $this = shift;
openlog( $this->{id}, "pid", "local1" );
}
sub closeSyslog()
sub closeSyslog
{
my $this = shift;
#closelog();
}
sub logFile( $ )
sub logFile
{
my $this = shift;
my $logFile = shift;
@ -546,18 +575,21 @@ sub logFile( $ )
}
}
sub openFile()
sub openFile
{
my $this = shift;
if ( open( LOGFILE, ">>".$this->{logFile} ) )
if ( open( $LOGFILE, ">>", $this->{logFile} ) )
{
LOGFILE->autoflush() if ( $this->{autoFlush} );
$LOGFILE->autoflush() if ( $this->{autoFlush} );
my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2];
my $webGid = (getgrnam( $Config{ZM_WEB_GROUP} ))[2];
if ( $> == 0 )
{
chown( $webUid, $webGid, $this->{logFile} ) or Fatal( "Can't change permissions on log file '".$this->{logFile}."': $!" )
chown( $webUid, $webGid, $this->{logFile} )
or Fatal( "Can't change permissions on log file '"
.$this->{logFile}."': $!"
)
}
}
else
@ -567,13 +599,13 @@ sub openFile()
}
}
sub closeFile()
sub closeFile
{
my $this = shift;
close( LOGFILE ) if ( fileno(LOGFILE) );
close( $LOGFILE ) if ( fileno($LOGFILE) );
}
sub logPrint( $;$ )
sub logPrint
{
my $this = shift;
my $level = shift;
@ -586,7 +618,17 @@ sub logPrint( $;$ )
my $code = $codes{$level};
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} )
{
$message = Carp::shortmess( $message );
@ -595,8 +637,9 @@ sub logPrint( $;$ )
{
$message = $message."\n";
}
syslog( $priorities{$level}, $code." [%s]", $string ) if ( $level <= $this->{syslogLevel} );
print( LOGFILE $message ) if ( $level <= $this->{fileLevel} );
syslog( $priorities{$level}, $code." [%s]", $string )
if ( $level <= $this->{syslogLevel} );
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} );
if ( $level <= $this->{databaseLevel} )
{
my $sql = "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )";
@ -606,7 +649,14 @@ sub logPrint( $;$ )
$this->{databaseLevel} = NOLOG;
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 )
{
$this->{databaseLevel} = NOLOG;
@ -624,7 +674,7 @@ sub logInit( ;@ )
$logger->initialise( %options );
}
sub logReinit()
sub logReinit
{
fetch()->reinitialise();
}
@ -636,7 +686,7 @@ sub logTerm
$logger = undef;
}
sub logHupHandler()
sub logHupHandler
{
my $savedErrno = $!;
return unless( $logger );
@ -645,47 +695,47 @@ sub logHupHandler()
$! = $savedErrno;
}
sub logSetSignal()
sub logSetSignal
{
$SIG{HUP} = \&logHupHandler;
}
sub logClearSignal()
sub logClearSignal
{
$SIG{HUP} = 'DEFAULT';
}
sub logLevel( ;$ )
sub logLevel
{
return( fetch()->level( @_ ) );
}
sub logDebugging()
sub logDebugging
{
return( fetch()->debugOn() );
}
sub logTermLevel( ;$ )
sub logTermLevel
{
return( fetch()->termLevel( @_ ) );
}
sub logDatabaseLevel( ;$ )
sub logDatabaseLevel
{
return( fetch()->databaseLevel( @_ ) );
}
sub logFileLevel( ;$ )
sub logFileLevel
{
return( fetch()->fileLevel( @_ ) );
}
sub logSyslogLevel( ;$ )
sub logSyslogLevel
{
return( fetch()->syslogLevel( @_ ) );
}
sub Mark( ;$$ )
sub Mark
{
my $level = shift;
$level = DEBUG unless( defined($level) );
@ -693,7 +743,7 @@ sub Mark( ;$$ )
fetch()->logPrint( $level, $tag );
}
sub Dump( \$;$ )
sub Dump
{
my $var = shift;
my $label = shift;
@ -756,11 +806,19 @@ ZoneMinder::Logger - ZoneMinder Logger module
=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
@ -768,7 +826,12 @@ Debug is discussed in terms of levels where 1 and above (currently only 1 for sc
=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
--------- --------- -----------
@ -805,27 +868,41 @@ These methods can be used to get and set the current settings as defined in logI
=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 );
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 );
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 );
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 );
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 );
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
=head2 EXPORT
@ -841,7 +918,8 @@ The :all tag will export all above symbols.
Carp
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

File diff suppressed because it is too large Load Diff

View File

@ -36,18 +36,18 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
zmMemKey
zmMemAttach
zmMemDetach
zmMemGet
zmMemPut
zmMemClean
) ],
'functions' => [ qw(
zmMemKey
zmMemAttach
zmMemDetach
zmMemGet
zmMemPut
zmMemClean
) ],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -68,117 +68,131 @@ use ZoneMinder::Logger qw(:all);
use Sys::Mmap;
sub zmMemKey( $ )
sub zmMemKey
{
my $monitor = shift;
my $monitor = shift;
return( defined($monitor->{MMapAddr})?$monitor->{MMapAddr}:undef );
}
sub zmMemAttach( $$ )
sub zmMemAttach
{
my ( $monitor, $size ) = @_;
if ( ! $size ) {
Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" );
return( undef );
}
if ( !defined($monitor->{MMapAddr}) )
{
my ( $monitor, $size ) = @_;
if ( ! $size ) {
Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" );
return( undef );
}
if ( !defined($monitor->{MMapAddr}) )
{
my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id};
if ( ! -e $mmap_file ) {
Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running.", $mmap_file ) );
return ( undef );
}
if ( ! -e $mmap_file ) {
Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running."
, $mmap_file
)
);
return ( undef );
}
my $mmap_file_size = -s $mmap_file;
my $mmap_file_size = -s $mmap_file;
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 ) );
return ( undef );
}
if ( !open( MMAP, "+<".$mmap_file ) )
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
)
);
return ( undef );
}
if ( !open( MMAP, "+<", $mmap_file ) )
{
Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) );
return( undef );
Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) );
return( undef );
}
my $mmap = undef;
my $mmap_addr = mmap( $mmap, $size, PROT_READ|PROT_WRITE, MAP_SHARED, \*MMAP );
if ( !$mmap_addr || !$mmap )
{
Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) );
close( MMAP );
return( undef );
Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) );
close( MMAP );
return( undef );
}
$monitor->{MMapHandle} = \*MMAP;
$monitor->{MMapAddr} = $mmap_addr;
$monitor->{MMap} = \$mmap;
}
return( !undef );
$monitor->{MMapHandle} = \*MMAP;
$monitor->{MMapAddr} = $mmap_addr;
$monitor->{MMap} = \$mmap;
}
return( !undef );
}
sub zmMemDetach( $ )
sub zmMemDetach
{
my $monitor = shift;
my $monitor = shift;
if ( $monitor->{MMap} )
{
if ( ! munmap( ${$monitor->{MMap}} ) ) {
Warn( "Unable to munmap for monitor $$monitor{Id}\n");
}
delete $monitor->{MMap};
Warn( "Unable to munmap for monitor $$monitor{Id}\n");
}
delete $monitor->{MMap};
}
if ( $monitor->{MMapAddr} )
{
delete $monitor->{MMapAddr};
delete $monitor->{MMapAddr};
}
if ( $monitor->{MMapHandle} )
{
close( $monitor->{MMapHandle} );
delete $monitor->{MMapHandle};
delete $monitor->{MMapHandle};
}
}
sub zmMemGet( $$$ )
sub zmMemGet
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $mmap = $monitor->{MMap};
my $mmap = $monitor->{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 );
}
my $data = substr( $$mmap, $offset, $size );
my $data = substr( $$mmap, $offset, $size );
return( $data );
}
sub zmMemPut( $$$$ )
sub zmMemPut
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
my $mmap = $monitor->{MMap};
my $mmap = $monitor->{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 );
}
substr( $$mmap, $offset, $size ) = $data;
return( !undef );
substr( $$mmap, $offset, $size ) = $data;
return( !undef );
}
sub zmMemClean
{
Debug( "Removing memory map files\n" );
Debug( "Removing memory map files\n" );
my $mapPath = $Config{ZM_PATH_MAP}."/zm.mmap.*";
foreach my $mapFile( glob( $mapPath ) )
{
( $mapFile ) = $mapFile =~ /^(.+)$/;
Debug( "Removing memory map file '$mapFile'\n" );
Debug( "Removing memory map file '$mapFile'\n" );
unlink( $mapFile );
}
}

View File

@ -19,7 +19,7 @@
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Memory::Shared;
@ -39,18 +39,18 @@ eval 'sub IPC_CREAT {0001000}' unless defined &IPC_CREAT;
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
'functions' => [ qw(
zmMemKey
zmMemAttach
zmMemDetach
zmMemGet
zmMemPut
zmMemClean
) ],
) ],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -69,82 +69,98 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
sub zmMemKey( $ )
sub zmMemKey
{
my $monitor = shift;
return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef );
my $monitor = shift;
return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef );
}
sub zmMemAttach( $$ )
sub zmMemAttach
{
my $monitor = shift;
my $size = shift;
if ( !defined($monitor->{ShmId}) )
{
my $shm_key = (hex($Config{ZM_SHM_KEY})&0xffff0000)|$monitor->{Id};
my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 );
if ( !defined($shm_id) )
{
Error( sprintf( "Can't get shared memory id '%x', %d: $!\n", $shm_key, $monitor->{Id} ) );
return( undef );
}
$monitor->{ShmKey} = $shm_key;
$monitor->{ShmId} = $shm_id;
}
return( !undef );
my $monitor = shift;
my $size = shift;
if ( !defined($monitor->{ShmId}) )
{
my $shm_key = (hex($Config{ZM_SHM_KEY})&0xffff0000)|$monitor->{Id};
my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 );
if ( !defined($shm_id) )
{
Error( sprintf( "Can't get shared memory id '%x', %d: $!\n"
, $shm_key
, $monitor->{Id}
)
);
return( undef );
}
$monitor->{ShmKey} = $shm_key;
$monitor->{ShmId} = $shm_id;
}
return( !undef );
}
sub zmMemDetach( $ )
sub zmMemDetach
{
my $monitor = shift;
my $monitor = shift;
delete $monitor->{ShmId};
delete $monitor->{ShmId};
}
sub zmMemGet( $$$ )
sub zmMemGet
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $shm_key = $monitor->{ShmKey};
my $shm_id = $monitor->{ShmId};
my $shm_key = $monitor->{ShmKey};
my $shm_id = $monitor->{ShmId};
my $data;
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( $data );
return( $data );
}
sub zmMemPut( $$$$ )
sub zmMemPut
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
my $shm_key = $monitor->{ShmKey};
my $shm_id = $monitor->{ShmId};
my $shm_key = $monitor->{ShmKey};
my $shm_id = $monitor->{ShmId};
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 );
}
sub zmMemClean
{
Debug( "Removing shared memory\n" );
# 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" );
open( CMD, "$command |" ) or Fatal( "Can't execute '$command': $!" );
while( <CMD> )
open( my $CMD, '<', "$command |" )
or Fatal( "Can't execute '$command': $!" );
while( <$CMD> )
{
chomp;
my ( $key, $id ) = split( /\s+/ );
@ -156,7 +172,7 @@ sub zmMemClean
qx( $command );
}
}
close( CMD );
close( $CMD );
}
1;

View File

@ -46,53 +46,53 @@ our $AUTOLOAD;
sub new
{
my $class = shift;
my $self = {};
$self->{readable} = !undef;
$self->{writeable} = !undef;
$self->{selectable} = undef;
$self->{state} = 'closed';
bless( $self, $class );
return $self;
my $class = shift;
my $self = {};
$self->{readable} = !undef;
$self->{writeable} = !undef;
$self->{selectable} = undef;
$self->{state} = 'closed';
bless( $self, $class );
return $self;
}
sub clone
{
my $self = shift;
my $clone = { %$self };
bless $clone, ref $self;
my $self = shift;
my $clone = { %$self };
bless $clone, ref $self;
}
sub open()
sub open
{
my $self = shift;
my $class = ref($self) or croak( "Can't get class for non object $self" );
croak( "Abstract base class method called for object of class $class" );
my $self = shift;
my $class = ref($self) or croak( "Can't get class for non object $self" );
croak( "Abstract base class method called for object of class $class" );
}
sub close()
sub close
{
my $self = shift;
my $class = ref($self) or croak( "Can't get class for non object $self" );
croak( "Abstract base class method called for object of class $class" );
my $self = shift;
my $class = ref($self) or croak( "Can't get class for non object $self" );
croak( "Abstract base class method called for object of class $class" );
}
sub getState()
sub getState
{
my $self = shift;
return( $self->{state} );
my $self = shift;
return( $self->{state} );
}
sub isOpen()
sub isOpen
{
my $self = shift;
return( $self->{state} eq "open" );
my $self = shift;
return( $self->{state} eq "open" );
}
sub isConnected()
sub isConnected
{
my $self = shift;
return( $self->{state} eq "connected" );
my $self = shift;
return( $self->{state} eq "connected" );
}
sub DESTROY
@ -101,15 +101,15 @@ sub DESTROY
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( !exists($self->{$name}) )
{
croak( "Can't access $name member of object of class $class" );
}
return( $self->{$name} );
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( !exists($self->{$name}) )
{
croak( "Can't access $name member of object of class $class" );
}
return( $self->{$name} );
}
1;

View File

@ -48,23 +48,23 @@ use Fcntl;
sub new
{
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel::Handle->new;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel::Handle->new;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
}
sub open()
sub open
{
my $self = shift;
local *sfh;
#sysopen( *sfh, $conn->{path}, O_NONBLOCK|O_RDONLY ) or croak( "Can't sysopen: $!" );
#open( *sfh, "<".$conn->{path} ) or croak( "Can't open: $!" );
open( *sfh, "+<".$self->{path} ) or croak( "Can't open: $!" );
$self->{state} = 'open';
$self->{handle} = *sfh;
my $self = shift;
local *sfh;
#sysopen( *sfh, $conn->{path}, O_NONBLOCK|O_RDONLY ) or croak( "Can't sysopen: $!" );
#open( *sfh, "<".$conn->{path} ) or croak( "Can't open: $!" );
open( *sfh, "+<", $self->{path} ) or croak( "Can't open: $!" );
$self->{state} = 'open';
$self->{handle} = *sfh;
}
1;

View File

@ -46,12 +46,12 @@ use POSIX;
sub new
{
my $class = shift;
my $port = shift;
my $self = ZoneMinder::Trigger::Channel->new();
$self->{handle} = undef;
bless( $self, $class );
return $self;
my $class = shift;
my $port = shift;
my $self = ZoneMinder::Trigger::Channel->new();
$self->{handle} = undef;
bless( $self, $class );
return $self;
}
sub spawns
@ -59,45 +59,51 @@ sub spawns
return( undef );
}
sub close()
sub close
{
my $self = shift;
close( $self->{handle} );
$self->{state} = 'closed';
$self->{handle} = undef;
my $self = shift;
close( $self->{handle} );
$self->{state} = 'closed';
$self->{handle} = undef;
}
sub read()
sub read
{
my $self = shift;
my $buffer;
my $nbytes = sysread( $self->{handle}, $buffer, POSIX::BUFSIZ );
if ( !$nbytes )
{
return( undef );
}
Debug( "Read '$buffer' ($nbytes bytes)\n" );
return( $buffer );
my $self = shift;
my $buffer;
my $nbytes = sysread( $self->{handle}, $buffer, POSIX::BUFSIZ );
if ( !$nbytes )
{
return( undef );
}
Debug( "Read '$buffer' ($nbytes bytes)\n" );
return( $buffer );
}
sub write()
sub write
{
my $self = shift;
my $buffer = shift;
my $nbytes = syswrite( $self->{handle}, $buffer );
if ( !defined( $nbytes) || $nbytes < length($buffer) )
{
Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".($nbytes?$nbytes:'undefined').": $!\n" );
return( undef );
}
Debug( "Wrote '$buffer' ($nbytes bytes)\n" );
return( !undef );
my $self = shift;
my $buffer = shift;
my $nbytes = syswrite( $self->{handle}, $buffer );
if ( !defined( $nbytes) || $nbytes < length($buffer) )
{
Error( "Unable to write buffer '".$buffer
.", expected "
.length($buffer)
." bytes, sent "
.($nbytes?$nbytes:'undefined')
.": $!\n"
);
return( undef );
}
Debug( "Wrote '$buffer' ($nbytes bytes)\n" );
return( !undef );
}
sub fileno()
sub fileno
{
my $self = shift;
return( defined($self->{handle})?fileno($self->{handle}):-1 );
my $self = shift;
return( defined($self->{handle})?fileno($self->{handle}):-1 );
}
1;

View File

@ -48,44 +48,45 @@ use Socket;
sub new
{
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel::Spawning->new();
$self->{selectable} = !undef;
$self->{port} = $params{port};
bless( $self, $class );
return $self;
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel::Spawning->new();
$self->{selectable} = !undef;
$self->{port} = $params{port};
bless( $self, $class );
return $self;
}
sub open()
sub open
{
my $self = shift;
local *sfh;
my $saddr = sockaddr_in( $self->{port}, INADDR_ANY );
socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or croak( "Can't open socket: $!" );
setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 );
bind( *sfh, $saddr ) or croak( "Can't bind: $!" );
listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" );
$self->{state} = 'open';
$self->{handle} = *sfh;
my $self = shift;
local *sfh;
my $saddr = sockaddr_in( $self->{port}, INADDR_ANY );
socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') )
or croak( "Can't open socket: $!" );
setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 );
bind( *sfh, $saddr ) or croak( "Can't bind: $!" );
listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" );
$self->{state} = 'open';
$self->{handle} = *sfh;
}
sub _spawn( $ )
sub _spawn
{
my $self = shift;
my $new_handle = shift;
my $clone = $self->clone();
$clone->{handle} = $new_handle;
$clone->{state} = 'connected';
return( $clone );
my $self = shift;
my $new_handle = shift;
my $clone = $self->clone();
$clone->{handle} = $new_handle;
$clone->{state} = 'connected';
return( $clone );
}
sub accept()
sub accept
{
my $self = shift;
local *cfh;
my $paddr = accept( *cfh, $self->{handle} );
return( $self->_spawn( *cfh ) );
my $self = shift;
local *cfh;
my $paddr = accept( *cfh, $self->{handle} );
return( $self->_spawn( *cfh ) );
}
1;

View File

@ -46,64 +46,70 @@ use Device::SerialPort;
sub new
{
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel->new;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel->new;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
}
sub open()
sub open
{
my $self = shift;
my $device = new Device::SerialPort( $self->{path} );
$device->baudrate(9600);
$device->databits(8);
$device->parity('none');
$device->stopbits(1);
$device->handshake('none');
my $self = shift;
my $device = new Device::SerialPort( $self->{path} );
$device->baudrate(9600);
$device->databits(8);
$device->parity('none');
$device->stopbits(1);
$device->handshake('none');
$device->read_const_time(50);
$device->read_char_time(10);
$device->read_const_time(50);
$device->read_char_time(10);
$self->{device} = $device;
$self->{state} = 'open';
$self->{state} = 'connected';
$self->{device} = $device;
$self->{state} = 'open';
$self->{state} = 'connected';
}
sub close()
sub close
{
my $self = shift;
$self->{device}->close();
$self->{state} = 'closed';
my $self = shift;
$self->{device}->close();
$self->{state} = 'closed';
}
sub read()
sub read
{
my $self = shift;
my $buffer = $self->{device}->lookfor();
if ( !$buffer || !length($buffer) )
{
return( undef );
}
Debug( "Read '$buffer' (".length($buffer)." bytes)\n" );
return( $buffer );
my $self = shift;
my $buffer = $self->{device}->lookfor();
if ( !$buffer || !length($buffer) )
{
return( undef );
}
Debug( "Read '$buffer' (".length($buffer)." bytes)\n" );
return( $buffer );
}
sub write()
sub write
{
my $self = shift;
my $buffer = shift;
my $nbytes = $self->{device}->write( $buffer );
$self->{device}->write_drain();
if ( !defined( $nbytes) || $nbytes < length($buffer) )
{
Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".$nbytes.": $!\n" );
return( undef );
}
Debug( "Wrote '$buffer' ($nbytes bytes)\n" );
return( !undef );
my $self = shift;
my $buffer = shift;
my $nbytes = $self->{device}->write( $buffer );
$self->{device}->write_drain();
if ( !defined( $nbytes) || $nbytes < length($buffer) )
{
Error( "Unable to write buffer '".$buffer
.", expected "
.length($buffer)
." bytes, sent "
.$nbytes
.": $!\n"
);
return( undef );
}
Debug( "Wrote '$buffer' ($nbytes bytes)\n" );
return( !undef );
}
1;

View File

@ -45,12 +45,12 @@ use ZoneMinder::Logger qw(:all);
sub new
{
my $class = shift;
my $port = shift;
my $self = ZoneMinder::Trigger::Channel::Handle->new();
$self->{spawns} = !undef;
bless( $self, $class );
return $self;
my $class = shift;
my $port = shift;
my $self = ZoneMinder::Trigger::Channel::Handle->new();
$self->{spawns} = !undef;
bless( $self, $class );
return $self;
}
sub spawns

View File

@ -48,28 +48,28 @@ use Socket;
sub new
{
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel->new;
$self->{selectable} = !undef;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
my $class = shift;
my %params = @_;
my $self = ZoneMinder::Trigger::Channel->new;
$self->{selectable} = !undef;
$self->{path} = $params{path};
bless( $self, $class );
return $self;
}
sub open()
sub open
{
my $self = shift;
local *sfh;
unlink( $self->{path} );
my $saddr = sockaddr_un( $self->{path} );
socket( *sfh, PF_UNIX, SOCK_STREAM, 0 ) or croak( "Can't open socket: $!" );
bind( *sfh, $saddr ) or croak( "Can't bind: $!" );
listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" );
$self->{handle} = *sfh;
my $self = shift;
local *sfh;
unlink( $self->{path} );
my $saddr = sockaddr_un( $self->{path} );
socket( *sfh, PF_UNIX, SOCK_STREAM, 0 ) or croak( "Can't open socket: $!" );
bind( *sfh, $saddr ) or croak( "Can't bind: $!" );
listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" );
$self->{handle} = *sfh;
}
sub _spawn( $ )
sub _spawn
{
my $self = shift;
my $new_handle = shift;
@ -79,7 +79,7 @@ sub _spawn( $ )
return( $clone );
}
sub accept()
sub accept
{
my $self = shift;
local *cfh;

View File

@ -46,115 +46,121 @@ our $AUTOLOAD;
sub new
{
my $class = shift;
my %params = @_;
my $self = {};
$self->{name} = $params{name};
$self->{channel} = $params{channel};
$self->{input} = $params{mode} =~ /r/i;
$self->{output} = $params{mode} =~ /w/i;
bless( $self, $class );
return $self;
my $class = shift;
my %params = @_;
my $self = {};
$self->{name} = $params{name};
$self->{channel} = $params{channel};
$self->{input} = $params{mode} =~ /r/i;
$self->{output} = $params{mode} =~ /w/i;
bless( $self, $class );
return $self;
}
sub clone
{
my $self = shift;
my $clone = { %$self };
bless $clone, ref $self;
return( $clone );
my $self = shift;
my $clone = { %$self };
bless $clone, ref $self;
return( $clone );
}
sub spawns
{
my $self = shift;
my $self = shift;
return( $self->{channel}->spawns() );
}
sub _spawn( $ )
sub _spawn
{
my $self = shift;
my $new_channel = shift;
my $clone = $self->clone();
$clone->{channel} = $new_channel;
return( $clone );
my $self = shift;
my $new_channel = shift;
my $clone = $self->clone();
$clone->{channel} = $new_channel;
return( $clone );
}
sub accept()
sub accept
{
my $self = shift;
my $new_channel = $self->{channel}->accept();
return( $self->_spawn( $new_channel ) );
my $self = shift;
my $new_channel = $self->{channel}->accept();
return( $self->_spawn( $new_channel ) );
}
sub open()
sub open
{
my $self = shift;
return( $self->{channel}->open() );
my $self = shift;
return( $self->{channel}->open() );
}
sub close()
sub close
{
my $self = shift;
return( $self->{channel}->close() );
my $self = shift;
return( $self->{channel}->close() );
}
sub fileno()
sub fileno
{
my $self = shift;
return( $self->{channel}->fileno() );
my $self = shift;
return( $self->{channel}->fileno() );
}
sub isOpen()
sub isOpen
{
my $self = shift;
return( $self->{channel}->isOpen() );
my $self = shift;
return( $self->{channel}->isOpen() );
}
sub isConnected()
sub isConnected
{
my $self = shift;
return( $self->{channel}->isConnected() );
my $self = shift;
return( $self->{channel}->isConnected() );
}
sub canRead()
sub canRead
{
my $self = shift;
return( $self->{input} && $self->isConnected() );
my $self = shift;
return( $self->{input} && $self->isConnected() );
}
sub canWrite()
sub canWrite
{
my $self = shift;
return( $self->{output} && $self->isConnected() );
my $self = shift;
return( $self->{output} && $self->isConnected() );
}
sub getMessages
{
my $self = shift;
my $buffer = $self->{channel}->read();
my $self = shift;
my $buffer = $self->{channel}->read();
return( undef ) if ( !defined($buffer) );
return( undef ) if ( !defined($buffer) );
my @messages = split( /\r?\n/, $buffer );
return( \@messages );
my @messages = split( /\r?\n/, $buffer );
return( \@messages );
}
sub putMessages
{
my $self = shift;
my $messages = shift;
my $self = shift;
my $messages = shift;
if ( @$messages )
{
my $buffer = join( "\n", @$messages );
$buffer .= "\n";
if ( !$self->{channel}->write( $buffer ) )
{
Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" );
}
}
return( undef );
if ( @$messages )
{
my $buffer = join( "\n", @$messages );
$buffer .= "\n";
if ( !$self->{channel}->write( $buffer ) )
{
Error( "Unable to write buffer '".$buffer
." to connection "
.$self->{name}
." ("
.$self->fileno()
.")\n"
);
}
}
return( undef );
}
sub timedActions
@ -167,22 +173,22 @@ sub DESTROY
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
elsif ( defined($self->{channel}) )
{
if ( exists($self->{channel}->{$name}) )
{
return( $self->{channel}->{$name} );
}
}
croak( "Can't access $name member of object of class $class" );
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
elsif ( defined($self->{channel}) )
{
if ( exists($self->{channel}->{$name}) )
{
return( $self->{channel}->{$name} );
}
}
croak( "Can't access $name member of object of class $class" );
}
1;

View File

@ -44,40 +44,40 @@ use ZoneMinder::Logger qw(:all);
sub new
{
my $class = shift;
my $path = shift;
my $self = ZoneMinder::Trigger::Connection->new( @_ );
bless( $self, $class );
return $self;
my $class = shift;
my $path = shift;
my $self = ZoneMinder::Trigger::Connection->new( @_ );
bless( $self, $class );
return $self;
}
sub getMessages
{
my $self = shift;
my $buffer = $self->{channel}->read();
my $self = shift;
my $buffer = $self->{channel}->read();
return( undef ) if ( !defined($buffer) );
return( undef ) if ( !defined($buffer) );
Debug( "Handling buffer '$buffer'\n" );
my @messages = grep { s/-/|/g; 1; } split( /\r?\n/, $buffer );
return( \@messages );
Debug( "Handling buffer '$buffer'\n" );
my @messages = grep { s/-/|/g; 1; } split( /\r?\n/, $buffer );
return( \@messages );
}
sub putMessages
{
my $self = shift;
my $messages = shift;
my $self = shift;
my $messages = shift;
if ( @$messages )
{
my $buffer = join( "\n", grep{ s/\|/-/; 1; } @$messages );
$buffer .= "\n";
if ( !$self->{channel}->write( $buffer ) )
{
Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" );
}
}
return( undef );
if ( @$messages )
{
my $buffer = join( "\n", grep{ s/\|/-/; 1; } @$messages );
$buffer .= "\n";
if ( !$self->{channel}->write( $buffer ) )
{
Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" );
}
}
return( undef );
}
1;

View File

@ -16,12 +16,12 @@ LOCKFILE=/var/lock/subsys/zm
loadconf()
{
if [ -f $ZM_CONFIG ]; then
. $ZM_CONFIG
else
echo "ERROR: $ZM_CONFIG not found."
return 1
fi
if [ -f $ZM_CONFIG ]; then
. $ZM_CONFIG
else
echo "ERROR: $ZM_CONFIG not found."
return 1
fi
}
loadconf
@ -30,95 +30,95 @@ command="$ZM_PATH_BIN/zmpkg.pl"
start()
{
# Commenting out as it is not needed. Leaving as a placeholder for future use.
# zmupdate || return $?
loadconf || return $?
#Make sure the directory for our PID folder exists or create one.
[ ! -d $pidfile ] \
&& mkdir -m 774 $pidfile \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile
#Make sure the folder for the socks file exists or create one
GetPath="select Value from Config where Name='ZM_PATH_SOCKS'"
# zmupdate || return $?
loadconf || return $?
#Make sure the directory for our PID folder exists or create one.
[ ! -d $pidfile ] \
&& mkdir -m 774 $pidfile \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile
#Make sure the folder for the socks file exists or create one
GetPath="select Value from Config where Name='ZM_PATH_SOCKS'"
dbHost=`echo $ZM_DB_HOST | cut -d: -f1`
dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2`
if [ "$dbPort" = "" ]
then
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
else
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
fi
[ ! -d $ZM_PATH_SOCK ] \
&& mkdir -m 774 $ZM_PATH_SOCK \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK
echo -n $"Starting $prog: "
$command start
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && touch $LOCKFILE
return $RETVAL
[ ! -d $ZM_PATH_SOCK ] \
&& mkdir -m 774 $ZM_PATH_SOCK \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK
echo -n $"Starting $prog: "
$command start
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && touch $LOCKFILE
return $RETVAL
}
stop()
{
loadconf
echo -n $"Stopping $prog: "
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && rm -f $LOCKFILE
loadconf
echo -n $"Stopping $prog: "
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && rm -f $LOCKFILE
}
zmstatus()
{
loadconf
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
$ZM_PATH_BIN/zmu -l
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
loadconf
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
$ZM_PATH_BIN/zmu -l
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
}
zmupdate()
{
if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then
$ZM_PATH_BIN/zmupdate.pl -f
fi
if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then
$ZM_PATH_BIN/zmupdate.pl -f
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'condrestart')
loadconf
result=`$ZM_PATH_BIN/zmdc.pl check`
if [ "$result" = "running" ]; then
$ZM_PATH_BIN/zmdc.pl shutdown > /dev/null
rm -f $LOCKFILE
start
fi
;;
'status')
status httpd
status mysqld
zmstatus
;;
*)
echo "Usage: $0 { start | stop | restart | condrestart | status }"
RETVAL=1
;;
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'condrestart')
loadconf
result=`$ZM_PATH_BIN/zmdc.pl check`
if [ "$result" = "running" ]; then
$ZM_PATH_BIN/zmdc.pl shutdown > /dev/null
rm -f $LOCKFILE
start
fi
;;
'status')
status httpd
status mysqld
zmstatus
;;
*)
echo "Usage: $0 { start | stop | restart | condrestart | status }"
RETVAL=1
;;
esac
exit $RETVAL

View File

@ -20,15 +20,33 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script checks for consistency between the event filesystem and
# the database. If events are found in one and not the other they are
# deleted (optionally). Additionally any monitor event directories that
# do not correspond to a database monitor are similarly disposed of.
# However monitors in the database that don't have a directory are left
# alone as this is valid if they are newly created and have no events
# yet.
#
=head1 NAME
zmaudit.pl - ZoneMinder event file system and database consistency checker
=head1 SYNOPSIS
zmaudit.pl [-r,-report|-i,-interactive]
=head1 DESCRIPTION
This script checks for consistency between the event filesystem and
the database. If events are found in one and not the other they are
deleted (optionally). Additionally any monitor event directories that
do not correspond to a database monitor are similarly disposed of.
However monitors in the database that don't have a directory are left
alone as this is valid if they are newly created and have no events
yet.
=head1 OPTIONS
-r, --report - Just report don't actually do anything
-i, --interactive - Ask before applying any changes
-c, --continuous - Run continuously
-v, --version - Print the installed version of ZoneMinder
=cut
use strict;
use bytes;
@ -38,7 +56,6 @@ use bytes;
#
# ==========================================================================
use constant MIN_AGE => 300; # Minimum age when we will delete anything
use constant MAX_AGED_DIRS => 10; # Number of event dirs to check age on
use constant RECOVER_TAG => "(r)"; # Tag to append to event name when recovered
use constant RECOVER_TEXT => "Recovered."; # Text to append to event notes when recovered
@ -56,9 +73,13 @@ use POSIX;
use File::Find;
use Time::HiRes qw/gettimeofday/;
use Getopt::Long;
use autouse 'Pod::Usage'=>qw(pod2usage);
use constant IMAGE_PATH => $Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_IMAGES};
use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS});
use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
? $Config{ZM_DIR_EVENTS}
: ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
;
$| = 1;
@ -71,39 +92,24 @@ my $interactive = 0;
my $continuous = 0;
my $version;
sub usage
{
print( "
Usage: zmaudit.pl [-r,-report|-i,-interactive]
Parameters are :-
-r, --report - Just report don't actually do anything
-i, --interactive - Ask before applying any changes
-c, --continuous - Run continuously
-v, --version - Print the installed version of ZoneMinder
");
exit( -1 );
}
sub aud_print( $ );
sub confirm( ;$$ );
sub deleteSwapImage();
logInit();
logSetSignal();
if ( !GetOptions( report=>\$report, interactive=>\$interactive, continuous=>\$continuous, version=>\$version ) )
{
usage();
}
GetOptions(
'report' =>\$report,
'interactive' =>\$interactive,
'continuous' =>\$continuous,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
if ( $version ) {
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
}
if ( ($report + $interactive + $continuous) > 1 )
{
print( STDERR "Error, only one option may be specified\n" );
usage();
pod2usage(-exitstatus => -1);
}
my $dbh = zmDbConnect();
@ -113,36 +119,46 @@ chdir( EVENT_PATH );
my $max_image_age = 6/24; # 6 hours
my $max_swap_age = 24/24; # 24 hours
my $image_path = IMAGE_PATH;
my $swap_image_path = $Config{ZM_PATH_SWAP};
my $loop = 1;
my $cleaned = 0;
MAIN: while( $loop ) {
while ( ! ( $dbh and $dbh->ping() ) ) {
$dbh = zmDbConnect();
while ( ! ( $dbh and $dbh->ping() ) ) {
$dbh = zmDbConnect();
last if $dbh;
if ( $continuous ) {
# if we are running continuously, then just skip to the next interval, otherwise we are a one off run, so wait a second and retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
sleep 1;
} # end if
} # end while can't connect to the db
last if $dbh;
if ( $continuous ) {
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
sleep 1;
} # end if
} # end while can't connect to the db
my $db_monitors;
if ( ! exists $Config{ZM_AUDIT_MIN_AGE} ) {
Fatal("ZM_AUDIT_MIN_AGE is not set in config.");
}
my $db_monitors;
my $monitorSelectSql = "select Id from Monitors order by Id";
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
my $eventSelectSql = "select Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age from Events where MonitorId = ? order by Id";
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() );
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
FROM Events WHERE MonitorId = ? ORDER BY Id";
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() );
$cleaned = 0;
my $res = $monitorSelectSth->execute() or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
my $res = $monitorSelectSth->execute()
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
while( my $monitor = $monitorSelectSth->fetchrow_hashref() )
{
Debug( "Found database monitor '$monitor->{Id}'" );
my $db_events = $db_monitors->{$monitor->{Id}} = {};
my $res = $eventSelectSth->execute( $monitor->{Id} ) or Fatal( "Can't execute: ".$eventSelectSth->errstr() );
my $res = $eventSelectSth->execute( $monitor->{Id} )
or Fatal( "Can't execute: ".$eventSelectSth->errstr() );
while ( my $event = $eventSelectSth->fetchrow_hashref() )
{
$db_events->{$event->{Id}} = $event->{Age};
@ -151,7 +167,7 @@ MAIN: while( $loop ) {
}
my $fs_monitors;
foreach my $monitor ( <[0-9]*> )
foreach my $monitor ( glob("[0-9]*") )
{
Debug( "Found filesystem monitor '$monitor'" );
my $fs_events = $fs_monitors->{$monitor} = {};
@ -159,12 +175,13 @@ MAIN: while( $loop ) {
if ( $Config{ZM_USE_DEEP_STORAGE} )
{
foreach my $day_dir ( <$monitor_dir/*/*/*> )
foreach my $day_dir ( glob("$monitor_dir/*/*/*") )
{
Debug( "Checking $day_dir" );
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
chdir( $day_dir );
opendir( DIR, "." ) or Fatal( "Can't open directory '$day_dir': $!" );
opendir( DIR, "." )
or Fatal( "Can't open directory '$day_dir': $!" );
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
closedir( DIR );
my $count = 0;
@ -231,7 +248,7 @@ MAIN: while( $loop ) {
{
while ( my ( $fs_event, $age ) = each(%$fs_events ) )
{
if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > MIN_AGE)) )
if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > $Config{ZM_AUDIT_MIN_AGE})) )
{
aud_print( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" );
if ( confirm() )
@ -256,7 +273,7 @@ MAIN: while( $loop ) {
}
my $monitor_links;
foreach my $link ( <*> )
foreach my $link ( glob("*") )
{
next if ( !-l $link );
next if ( -e $link );
@ -274,13 +291,17 @@ MAIN: while( $loop ) {
$cleaned = 0;
my $deleteMonitorSql = "delete low_priority from Monitors where Id = ?";
my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql ) or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() );
my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql )
or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() );
my $deleteEventSql = "delete low_priority from Events where Id = ?";
my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql ) or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() );
my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql )
or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() );
my $deleteFramesSql = "delete low_priority from Frames where EventId = ?";
my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql ) or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() );
my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql )
or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() );
my $deleteStatsSql = "delete low_priority from Stats where EventId = ?";
my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql ) or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() );
my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql )
or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() );
while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) )
{
if ( my $fs_events = $fs_monitors->{$db_monitor} )
@ -289,27 +310,33 @@ MAIN: while( $loop ) {
{
while ( my ( $db_event, $age ) = each(%$db_events ) )
{
if ( !defined($fs_events->{$db_event}) && ($age > MIN_AGE) )
{
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" );
if ( confirm() )
{
my $res = $deleteEventSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteFramesSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$res = $deleteStatsSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
if ( !defined($fs_events->{$db_event}) ) {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" );
if ( confirm() ) {
my $res = $deleteEventSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteFramesSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$res = $deleteStatsSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
} else {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem but too young to delete." );
}
}
}
}
}
else
{
#aud_print( "Database monitor '$db_monitor' does not exist in filesystem" );
aud_print( "Database monitor '$db_monitor' does not exist in filesystem" );
#if ( confirm() )
#{
# We don't actually do this in case it's new
#my $res = $deleteMonitorSth->execute( $db_monitor ) or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() );
#my $res = $deleteMonitorSth->execute( $db_monitor )
# or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() );
#$cleaned = 1;
#}
}
@ -318,15 +345,20 @@ MAIN: while( $loop ) {
# Remove orphaned events (with no monitor)
$cleaned = 0;
my $selectOrphanedEventsSql = "select Events.Id, Events.Name from Events left join Monitors on (Events.MonitorId = Monitors.Id) where isnull(Monitors.Id)";
my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql ) or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
$res = $selectOrphanedEventsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
my $selectOrphanedEventsSql = "SELECT Events.Id, Events.Name
FROM Events LEFT JOIN Monitors ON (Events.MonitorId = Monitors.Id)
WHERE isnull(Monitors.Id)";
my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql )
or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
$res = $selectOrphanedEventsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() )
{
aud_print( "Found orphaned event with no monitor '$event->{Id}'" );
if ( confirm() )
{
$res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
}
@ -334,15 +366,19 @@ MAIN: while( $loop ) {
# Remove empty events (with no frames)
$cleaned = 0;
my $selectEmptyEventsSql = "select * from Events as E left join Frames as F on (E.Id = F.EventId) where isnull(F.EventId) and now() - interval ".MIN_AGE." second > E.StartTime";
my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
$res = $selectEmptyEventsSth->execute() or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() );
my $selectEmptyEventsSql = "SELECT * FROM Events as E LEFT JOIN Frames as F ON (E.Id = F.EventId)
WHERE isnull(F.EventId) AND now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second > E.StartTime";
my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql )
or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
$res = $selectEmptyEventsSth->execute()
or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() );
while( my $event = $selectEmptyEventsSth->fetchrow_hashref() )
{
aud_print( "Found empty event with no frame records '$event->{Id}'" );
if ( confirm() )
{
$res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
}
@ -350,15 +386,19 @@ MAIN: while( $loop ) {
# Remove orphaned frame records
$cleaned = 0;
my $selectOrphanedFramesSql = "select distinct EventId from Frames where EventId not in (select Id from Events)";
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute() or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() );
my $selectOrphanedFramesSql = "SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)";
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() );
while( my $frame = $selectOrphanedFramesSth->fetchrow_hashref() )
{
aud_print( "Found orphaned frame records for event '$frame->{EventId}'" );
if ( confirm() )
{
$res = $deleteFramesSth->execute( $frame->{EventId} ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$res = $deleteFramesSth->execute( $frame->{EventId} )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$cleaned = 1;
}
}
@ -366,32 +406,84 @@ MAIN: while( $loop ) {
# Remove orphaned stats records
$cleaned = 0;
my $selectOrphanedStatsSql = "select distinct EventId from Stats where EventId not in (select Id from Events)";
my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql ) or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() );
$res = $selectOrphanedStatsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() );
my $selectOrphanedStatsSql = "SELECT DISTINCT EventId FROM Stats
WHERE EventId NOT IN (SELECT Id FROM Events)";
my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql )
or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() );
$res = $selectOrphanedStatsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() );
while( my $stat = $selectOrphanedStatsSth->fetchrow_hashref() )
{
aud_print( "Found orphaned statistic records for event '$stat->{EventId}'" );
if ( confirm() )
{
$res = $deleteStatsSth->execute( $stat->{EventId} ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$res = $deleteStatsSth->execute( $stat->{EventId} )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# New audit to close any events that were left open for longer than MIN_AGE seconds
my $selectUnclosedEventsSql = "select E.Id, max(F.TimeStamp) as EndTime, unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, max(F.FrameId) as Frames, count(if(F.Score>0,1,NULL)) as AlarmFrames, sum(F.Score) as TotScore, max(F.Score) as MaxScore, M.EventPrefix as Prefix from Events as E left join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where isnull(E.Frames) or isnull(E.EndTime) group by E.Id having EndTime < (now() - interval ".MIN_AGE." second)";
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql ) or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
my $updateUnclosedEventsSql = "update low_priority Events set Name = ?, EndTime = ?, Length = ?, Frames = ?, AlarmFrames = ?, TotScore = ?, AvgScore = ?, MaxScore = ?, Notes = concat_ws( ' ', Notes, ? ) where Id = ?";
my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql ) or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() );
$res = $selectUnclosedEventsSth->execute() or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
my $selectUnclosedEventsSql =
"SELECT E.Id,
max(F.TimeStamp) as EndTime,
unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
max(F.FrameId) as Frames,
count(if(F.Score>0,1,NULL)) as AlarmFrames,
sum(F.Score) as TotScore,
max(F.Score) as MaxScore,
M.EventPrefix as Prefix
FROM Events as E
LEFT JOIN Monitors as M on E.MonitorId = M.Id
INNER JOIN Frames as F on E.Id = F.EventId
WHERE isnull(E.Frames) or isnull(E.EndTime)
GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
;
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
my $updateUnclosedEventsSql =
"UPDATE low_priority Events
SET Name = ?,
EndTime = ?,
Length = ?,
Frames = ?,
AlarmFrames = ?,
TotScore = ?,
AvgScore = ?,
MaxScore = ?,
Notes = concat_ws( ' ', Notes, ? )
WHERE Id = ?"
;
my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql )
or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() );
$res = $selectUnclosedEventsSth->execute()
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() )
{
aud_print( "Found open event '$event->{Id}'" );
if ( confirm( 'close', 'closing' ) )
{
$res = $updateUnclosedEventsSth->execute( sprintf( "%s%d%s", $event->{Prefix}, $event->{Id}, RECOVER_TAG ), $event->{EndTime}, $event->{Length}, $event->{Frames}, $event->{AlarmFrames}, $event->{TotScore}, $event->{AlarmFrames}?int($event->{TotScore}/$event->{AlarmFrames}):0, $event->{MaxScore}, RECOVER_TEXT, $event->{Id} ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
$res = $updateUnclosedEventsSth->execute
(
sprintf("%s%d%s",
$event->{Prefix},
$event->{Id},
RECOVER_TAG
),
$event->{EndTime},
$event->{Length},
$event->{Frames},
$event->{AlarmFrames},
$event->{TotScore},
$event->{AlarmFrames}
? int($event->{TotScore} / $event->{AlarmFrames})
: 0
,
$event->{MaxScore},
RECOVER_TEXT,
$event->{Id}
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
}
}
@ -405,7 +497,7 @@ MAIN: while( $loop ) {
}
# Now delete any old swap files
( my $swap_image_root ) = ( $swap_image_path =~ /^(.*)$/ ); # De-taint
( my $swap_image_root ) = ( $Config{ZM_PATH_SWAP} =~ /^(.*)$/ ); # De-taint
File::Find::find( { wanted=>\&deleteSwapImage, untaint=>1 }, $swap_image_root );
# Prune the Logs table if required
@ -414,26 +506,43 @@ MAIN: while( $loop ) {
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ )
{
# Number of rows
my $selectLogRowCountSql = "select count(*) as Rows from Logs";
my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql ) or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() );
$res = $selectLogRowCountSth->execute() or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() );
my $selectLogRowCountSql = "SELECT count(*) as Rows from Logs";
my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql )
or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() );
$res = $selectLogRowCountSth->execute()
or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() );
my $row = $selectLogRowCountSth->fetchrow_hashref();
my $logRows = $row->{Rows};
if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} )
{
my $deleteLogByRowsSql = "delete low_priority from Logs order by TimeKey asc limit ?";
my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql ) or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() );
$res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} ) or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() );
aud_print( "Deleted ".$deleteLogByRowsSth->rows()." log table entries by count\n" ) if ( $deleteLogByRowsSth->rows() );
my $deleteLogByRowsSql = "DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?";
my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql )
or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() );
$res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} )
or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() );
if ( $deleteLogByRowsSth->rows() )
{
aud_print( "Deleted ".$deleteLogByRowsSth->rows()
." log table entries by count\n" )
;
}
}
}
else
{
# Time of record
my $deleteLogByTimeSql = "delete low_priority from Logs where TimeKey < unix_timestamp(now() - interval ".$Config{ZM_LOG_DATABASE_LIMIT}.")";
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql ) or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute() or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
aud_print( "Deleted ".$deleteLogByTimeSth->rows()." log table entries by time\n" ) if ( $deleteLogByTimeSth->rows() );
my $deleteLogByTimeSql =
"DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval ".$Config{ZM_LOG_DATABASE_LIMIT}.")";
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute()
or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
if ( $deleteLogByTimeSth->rows() ){
aud_print( "Deleted ".$deleteLogByTimeSth->rows()
." log table entries by time\n" )
;
}
}
}
$loop = $continuous;
@ -443,7 +552,7 @@ MAIN: while( $loop ) {
exit( 0 );
sub aud_print( $ )
sub aud_print
{
my $string = shift;
if ( !$continuous )
@ -456,7 +565,7 @@ sub aud_print( $ )
}
}
sub confirm( ;$$ )
sub confirm
{
my $prompt = shift || "delete";
my $action = shift || "deleting";
@ -496,7 +605,7 @@ sub confirm( ;$$ )
return( $yesno );
}
sub deleteSwapImage()
sub deleteSwapImage
{
my $file = $_;

View File

@ -20,12 +20,43 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script provides a way to import new ptz camera controls & camera presets
# into existing zoneminder systems. This script also provides a way to export
# ptz camera controls & camera presets from an existing zoneminder system into
# a sql file, which can then be easily imported to another zoneminder system.
#
=head1 NAME
zmcamtool.pl - ZoneMinder tool to import camera controls and presets
=head1 SYNOPSIS
zmcamtool.pl [--user=<dbuser> --pass=<dbpass>]
[--import [file.sql] [--overwrite]]
[--export [name]]
[--topreset id [--noregex]]
=head1 DESCRIPTION
This script provides a way to import new ptz camera controls & camera presets
into existing zoneminder systems. This script also provides a way to export
ptz camera controls & camera presets from an existing zoneminder system into
a sql file, which can then be easily imported to another zoneminder system.
=head1 OPTIONS
--export - Export all camera controls and presets to STDOUT.
Optionally specify a control or preset name.
--import [file.sql] - Import new camera controls and presets found in
zm_create.sql into the ZoneMinder dB.
Optionally specify an alternate sql file to read from.
--overwrite - Overwrite any existing controls or presets.
with the same name as the new controls or presets.
--topreset id - Copy a monitor to a Camera Preset given the monitor id.
--noregex - Do not try to find and replace fields such as usernames,
passwords, IP addresses, etc with generic placeholders
when converting a monitor to a preset.
--help - Print usage information.
--user=<dbuser> - Alternate dB user with privileges to alter dB.
--pass=<dbpass> - Password of alternate dB user with privileges to alter dB.
=cut
use strict;
use bytes;
@ -35,6 +66,7 @@ use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database qw(:all);
use DBI;
use Getopt::Long;
use autouse 'Pod::Usage'=>qw(pod2usage);
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
@ -57,256 +89,258 @@ my $dbUser = $Config{ZM_DB_USER};
my $dbPass = $Config{ZM_DB_PASS};
my $version = 0;
# Process commandline parameters with getopt long
if ( !GetOptions( 'export'=>\$export, 'import'=>\$import, 'overwrite'=>\$overwrite, 'help'=>\$help, 'topreset'=>\$topreset, 'noregex'=>\$noregex, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass, 'version'=>\$version ) ) {
Usage();
}
GetOptions(
'export' =>\$export,
'import' =>\$import,
'overwrite' =>\$overwrite,
'help' =>\$help,
'topreset' =>\$topreset,
'noregex' =>\$noregex,
'user:s' =>\$dbUser,
'pass:s' =>\$dbPass,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
$Config{ZM_DB_USER} = $dbUser;
$Config{ZM_DB_PASS} = $dbPass;
if ( $version ) {
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
}
# Check to make sure commandline params make sense
if ( ((!$help) && ($import + $export + $topreset) != 1 )) {
print( STDERR qq/Please give only one of the following: "import", "export", or "topreset".\n/ );
Usage();
print( STDERR qq/Please give only one of the following: "import", "export", or "topreset".\n/ );
pod2usage(-exitstatus => -1);
}
if ( ($export)&&($overwrite) ) {
print( "Warning: Overwrite parameter ignored during an export.\n");
print( "Warning: Overwrite parameter ignored during an export.\n");
}
if ( ($noregex)&&(!$topreset) ) {
print( qq/Warning: Noregex parameter only applies when "topreset" parameter is also set. Ignoring.\n/);
print( qq/Warning: Noregex parameter only applies when "topreset" parameter is also set. Ignoring.\n/);
}
if ( ($topreset)&&($ARGV[0] !~ /\d\d*/) ) {
print( STDERR qq/Parameter "topreset" requires a valid monitor ID.\n/ );
Usage();
print( STDERR qq/Parameter "topreset" requires a valid monitor ID.\n/ );
pod2usage(-exitstatus => -1);
}
# Call the appropriate subroutine based on the params given on the commandline
if ($help) {
Usage();
pod2usage(-exitstatus => -1);
}
if ($export) {
exportsql();
exportsql();
}
if ($import) {
importsql();
importsql();
}
if ($topreset) {
toPreset();
toPreset();
}
###############
# SUBROUTINES #
###############
# Usage subroutine help text
sub Usage
{
die("
USAGE:
zmcamtool.pl [--user=<dbuser> --pass=<dbpass>]
[--import [file.sql] [--overwrite]]
[--export [name]]
[--topreset id [--noregex]]
PARAMETERS:
--export - Export all camera controls and presets to STDOUT.
Optionally specify a control or preset name.
--import [file.sql] - Import new camera controls and presets found in
zm_create.sql into the ZoneMinder dB.
Optionally specify an alternate sql file to read from.
--overwrite - Overwrite any existing controls or presets.
with the same name as the new controls or presets.
--topreset id - Copy a monitor to a Camera Preset given the monitor id.
--noregex - Do not try to find and replace fields such as usernames,
passwords, ip addresses, etc with generic placeholders
when converting a monitor to a preset.
--help - Print usage information.
--user=<dbuser> - Alternate dB user with privileges to alter dB.
--pass=<dbpass> - Password of alternate dB user with privileges to alter dB.
\n");
}
# Execute a pre-built sql select query
sub selectQuery
{
my $dbh = shift;
my $sql = shift;
my $monitorid = shift;
my $dbh = shift;
my $sql = shift;
my $monitorid = shift;
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($monitorid) or die( "Can't execute: ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($monitorid)
or die( "Can't execute: ".$sth->errstr() );
my @data = $sth->fetchrow_array();
$sth->finish();
my @data = $sth->fetchrow_array();
$sth->finish();
return @data;
return @data;
}
# Exectute a pre-built sql query
sub runQuery
{
my $dbh = shift;
my $sql = shift;
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
my $dbh = shift;
my $sql = shift;
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
return $res;
return $res;
}
# Build and execute a sql insert query
sub insertQuery
{
my $dbh = shift;
my $tablename = shift;
my @data = @_;
my $dbh = shift;
my $tablename = shift;
my @data = @_;
my $sql = "insert into $tablename values (NULL,".(join ", ", ("?") x @data).")"; # Add "?" for each array element
my $sql = "INSERT INTO $tablename VALUES (NULL,"
.(join ", ", ("?") x @data).")"; # Add "?" for each array element
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute(@data) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute(@data)
or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
return $res;
return $res;
}
# Build and execute a sql delete query
sub deleteQuery
{
my $dbh = shift;
my $sqltable = shift;
my $sqlname = shift;
my $dbh = shift;
my $sqltable = shift;
my $sqlname = shift;
my $sql = "delete from $sqltable where Name = ?";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($sqlname) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
my $sql = "DELETE FROM $sqltable WHERE Name = ?";
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($sqlname)
or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
return $res;
return $res;
}
# Build and execute a sql select count query
sub checkExists
{
my $dbh = shift;
my $sqltable = shift;
my $sqlname = shift;
my $result = 0;
my $dbh = shift;
my $sqltable = shift;
my $sqlname = shift;
my $result = 0;
my $sql = "select count(*) from $sqltable where Name = ?";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($sqlname) or die( "Can't execute: ".$sth->errstr() );
my $sql = "SELECT count(*) FROM $sqltable WHERE Name = ?";
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute($sqlname)
or die( "Can't execute: ".$sth->errstr() );
my $rows = $sth->fetchrow_arrayref();
$sth->finish();
my $rows = $sth->fetchrow_arrayref();
$sth->finish();
if ($rows->[0] > 0) {
$result = 1;
}
if ($rows->[0] > 0) {
$result = 1;
}
return $result;
return $result;
}
# Import camera control & presets into the zoneminder dB
sub importsql
{
my @newcontrols;
my @overwritecontrols;
my @skippedcontrols;
my @newpresets;
my @overwritepresets;
my @skippedpresets;
my %controls;
my %monitorpresets;
my @newcontrols;
my @overwritecontrols;
my @skippedcontrols;
my @newpresets;
my @overwritepresets;
my @skippedpresets;
my %controls;
my %monitorpresets;
if ($ARGV[0]) {
$sqlfile = $ARGV[0];
} else {
$sqlfile = $Config{ZM_PATH_DATA}.'/db/zm_create.sql';
}
if ($ARGV[0]) {
$sqlfile = $ARGV[0];
} else {
$sqlfile = $Config{ZM_PATH_DATA}.'/db/zm_create.sql';
}
open(my $SQLFILE,"<",$sqlfile) or die( "Can't Open file: $!\n" );
open(my $SQLFILE,"<",$sqlfile)
or die( "Can't Open file: $!\n" );
# Find and extract ptz control and monitor preset records
while (<$SQLFILE>) {
# Our regex replaces the primary key with NULL
if (s/^(INSERT INTO .*?Controls.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) {
$controls{$3} = $_;
} elsif (s/^(INSERT INTO .*?MonitorPresets.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) {
$monitorpresets{$3} = $_;
}
}
close $SQLFILE;
# Find and extract ptz control and monitor preset records
while (<$SQLFILE>) {
# Our regex replaces the primary key with NULL
if (s/^(INSERT INTO .*?Controls.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) {
$controls{$3} = $_;
} elsif (s/^(INSERT INTO .*?MonitorPresets.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) {
$monitorpresets{$3} = $_;
}
}
close $SQLFILE;
if ( ! (%controls || %monitorpresets) ) {
die( "Error: No relevant data found in $sqlfile.\n" );
}
if ( ! (%controls || %monitorpresets) ) {
die( "Error: No relevant data found in $sqlfile.\n" );
}
# Now that we've got what we were looking for, compare to what is already in the dB
# Now that we've got what we were looking for,
# compare to what is already in the dB
my $dbh = zmDbConnect();
foreach (keys %controls) {
if (!checkExists($dbh,"Controls",$_)) {
# No existing Control was found. Add new control to dB.
runQuery($dbh,$controls{$_});
push @newcontrols, $_;
} elsif ($overwrite) {
# An existing Control was found and the overwrite flag is set. Overwrite the control.
deleteQuery($dbh,"Controls",$_);
runQuery($dbh,$controls{$_});
push @overwritecontrols, $_;
} else {
# An existing Control was found and the overwrite flag was not set. Do nothing.
push @skippedcontrols, $_;
}
}
my $dbh = zmDbConnect();
foreach (keys %controls) {
if (!checkExists($dbh,"Controls",$_)) {
# No existing Control was found. Add new control to dB.
runQuery($dbh,$controls{$_});
push @newcontrols, $_;
} elsif ($overwrite) {
# An existing Control was found and the overwrite flag is set.
# Overwrite the control.
deleteQuery($dbh,"Controls",$_);
runQuery($dbh,$controls{$_});
push @overwritecontrols, $_;
} else {
# An existing Control was found and the overwrite flag was not set.
# Do nothing.
push @skippedcontrols, $_;
}
}
foreach (keys %monitorpresets) {
if (!checkExists($dbh,"MonitorPresets",$_)) {
# No existing MonitorPreset was found. Add new MonitorPreset to dB.
runQuery($dbh,$monitorpresets{$_});
push @newpresets, $_;
} elsif ($overwrite) {
# An existing MonitorPreset was found and the overwrite flag is set. Overwrite the MonitorPreset.
deleteQuery($dbh,"MonitorPresets",$_);
runQuery($dbh,$monitorpresets{$_});
push @overwritepresets, $_;
} else {
# An existing MonitorPreset was found and the overwrite flag was not set. Do nothing.
push @skippedpresets, $_;
}
}
foreach (keys %monitorpresets) {
if (!checkExists($dbh,"MonitorPresets",$_)) {
# No existing MonitorPreset was found. Add new MonitorPreset to dB.
runQuery($dbh,$monitorpresets{$_});
push @newpresets, $_;
} elsif ($overwrite) {
# An existing MonitorPreset was found and the overwrite flag is set.
# Overwrite the MonitorPreset.
deleteQuery($dbh,"MonitorPresets",$_);
runQuery($dbh,$monitorpresets{$_});
push @overwritepresets, $_;
} else {
# An existing MonitorPreset was found and the overwrite flag was
# not set. Do nothing.
push @skippedpresets, $_;
}
}
if (@newcontrols) {
print "Number of ptz camera controls added: ".scalar(@newcontrols)."\n";
}
if (@overwritecontrols) {
print "Number of existing ptz camera controls overwritten: ".scalar(@overwritecontrols)."\n";
}
if (@skippedcontrols) {
print "Number of existing ptz camera controls skipped: ".scalar(@skippedcontrols)."\n";
}
if (@newcontrols) {
print "Number of ptz camera controls added: "
.scalar(@newcontrols)."\n";
}
if (@overwritecontrols) {
print "Number of existing ptz camera controls overwritten: "
.scalar(@overwritecontrols)."\n";
}
if (@skippedcontrols) {
print "Number of existing ptz camera controls skipped: "
.scalar(@skippedcontrols)."\n";
}
if (@newpresets) {
print "Number of monitor presets added: ".scalar(@newpresets)."\n";
}
if (@overwritepresets) {
print "Number of existing monitor presets overwritten: ".scalar(@overwritepresets)."\n";
}
if (@skippedpresets) {
print "Number of existing presets skipped: ".scalar(@skippedpresets)."\n";
}
if (@newpresets) {
print "Number of monitor presets added: "
.scalar(@newpresets)."\n";
}
if (@overwritepresets) {
print "Number of existing monitor presets overwritten: "
.scalar(@overwritepresets)."\n";
}
if (@skippedpresets) {
print "Number of existing presets skipped: "
.scalar(@skippedpresets)."\n";
}
}
# Export camera controls & presets from the zoneminder dB to STDOUT
@ -317,14 +351,14 @@ my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
my $command = "mysqldump -t --skip-opt --compact -h".$host;
$command .= " -P".$port if defined($port);
if ( $dbUser ) {
$command .= " -u".$dbUser;
if ( $dbPass ) {
$command .= " -p".$dbPass;
}
}
$command .= " -u".$dbUser;
if ( $dbPass ) {
$command .= " -p".$dbPass;
}
}
if ($ARGV[0]) {
$command .= qq( --where="Name = '$ARGV[0]'");
$command .= qq( --where="Name = '$ARGV[0]'");
}
$command .= " zm Controls MonitorPresets";
@ -332,78 +366,81 @@ $command .= " zm Controls MonitorPresets";
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
chomp( $output );
print( "Output: $output\n" );
chomp( $output );
print( "Output: $output\n" );
}
if ( $status ) {
die( "Command '$command' exited with status: $status\n" );
die( "Command '$command' exited with status: $status\n" );
} else {
# NULLify the primary keys before printing the output to STDOUT
$output =~ s/VALUES \((.*?),'/VALUES \(NULL,'/ig;
print $output;
}
# NULLify the primary keys before printing the output to STDOUT
$output =~ s/VALUES \((.*?),'/VALUES \(NULL,'/ig;
print $output;
}
}
sub toPreset
{
my $dbh = zmDbConnect();
my $monitorid = $ARGV[0];
my $dbh = zmDbConnect();
my $monitorid = $ARGV[0];
# Grap the following fields from the Monitors table
my $sql = "select
Name,
Type,
Device,
Channel,
Format,
Protocol,
Method,
Host,
Port,
Path,
SubPath,
Width,
Height,
Palette,
MaxFPS,
Controllable,
ControlId,
ControlDevice,
ControlAddress,
DefaultRate,
DefaultScale
from Monitors where Id = ?";
my @data = selectQuery($dbh,$sql,$monitorid);
# Grap the following fields from the Monitors table
my $sql = "SELECT
Name,
Type,
Device,
Channel,
Format,
Protocol,
Method,
Host,
Port,
Path,
SubPath,
Width,
Height,
Palette,
MaxFPS,
Controllable,
ControlId,
ControlDevice,
ControlAddress,
DefaultRate,
DefaultScale
FROM Monitors WHERE Id = ?";
my @data = selectQuery($dbh,$sql,$monitorid);
if (!@data) {
die( "Error: Monitor Id $monitorid does not appear to exist in the database.\n" );
}
if (!@data) {
die( "Error: Monitor Id $monitorid does not appear to exist in the database.\n" );
}
# Attempt to search for and replace system specific values such as ip addresses, ports, usernames, etc. with generic placeholders
if (!$noregex) {
foreach (@data) {
s/\b(?:\d{1,3}\.){3}\d{1,3}\b/<ip-address>/; # ip address
s/<ip-address>:(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$/<ip-address>:<port>/; # tcpip port
s/\/\/.*:.*@/\/\/<username>:<pwd>@/; # user & pwd preceeding an ip address
s/(&|\?)(user|username)=\w\w*(&|\?)/$1$2=<username>$3/i; # username embeded in url
s/(&|\?)(pwd|password)=\w\w*(&|\?)/$1$2=<pwd>$3/i; # password embeded in url
s/\w\w*:\w\w*/<username>:<pwd>/; # user & pwd in their own field
s/\/dev\/video\d\d*/\/dev\/video<?>/; # local video devices
}
}
# Attempt to search for and replace system specific values such as
# ip addresses, ports, usernames, etc. with generic placeholders
if (!$noregex) {
foreach (@data) {
s/\b(?:\d{1,3}\.){3}\d{1,3}\b/<ip-address>/; # ip address
s/<ip-address>:(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$/<ip-address>:<port>/; # tcpip port
s/\/\/.*:.*@/\/\/<username>:<pwd>@/; # user & pwd preceeding an ip address
s/(&|\?)(user|username)=\w\w*(&|\?)/$1$2=<username>$3/i; # username embeded in url
s/(&|\?)(pwd|password)=\w\w*(&|\?)/$1$2=<pwd>$3/i; # password embeded in url
s/\w\w*:\w\w*/<username>:<pwd>/; # user & pwd in their own field
s/\/dev\/video\d\d*/\/dev\/video<?>/; # local video devices
}
}
if (!checkExists($dbh,"MonitorPresets",$data[0])) {
# No existing Preset was found. Add new Preset to dB.
print "Adding new preset: $data[0]\n";
insertQuery($dbh,"MonitorPresets",@data);
} elsif ($overwrite) {
# An existing Control was found and the overwrite flag is set. Overwrite the control.
print "Existing preset $data[0] detected.\nOverwriting...\n";
deleteQuery($dbh,"MonitorPresets",$data[0]);
insertQuery($dbh,"MonitorPresets",@data);
} else {
# An existing Control was found and the overwrite flag was not set. Do nothing.
print "Existing preset $data[0] detected and overwrite flag not set.\nSkipping...\n";
}
if (!checkExists($dbh,"MonitorPresets",$data[0])) {
# No existing Preset was found. Add new Preset to dB.
print "Adding new preset: $data[0]\n";
insertQuery($dbh,"MonitorPresets",@data);
} elsif ($overwrite) {
# An existing Control was found and the overwrite flag is set.
# Overwrite the control.
print "Existing preset $data[0] detected.\nOverwriting...\n";
deleteQuery($dbh,"MonitorPresets",$data[0]);
insertQuery($dbh,"MonitorPresets",@data);
} else {
# An existing Control was found and the overwrite flag was not set.
# Do nothing.
print "Existing preset $data[0] detected and overwrite flag not set.\nSkipping...\n";
}
}

View File

@ -20,16 +20,39 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script continuously monitors the recorded events for the given
# monitor and applies any filters which would delete and/or upload
# matching events
#
=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
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;
@ -44,14 +67,6 @@ $ENV{PATH} = '/bin:/usr/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage
{
print( "
Usage: zmcontrol.pl --id <monitor_id> --command=<command> <various options>
");
exit();
}
logInit();
my $arg_string = join( " ", @ARGV );
@ -59,29 +74,25 @@ my $arg_string = join( " ", @ARGV );
my $id;
my %options;
if ( !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},
)
)
{
Usage();
}
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" );
Usage();
pod2usage(-exitstatus => -1);
}
( $id ) = $id =~ /^(\w+)$/;
@ -90,13 +101,14 @@ Debug( $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: $!" );
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
# The server isn't there
my $monitor = zmDbGetMonitorAndControl( $id );
if ( !$monitor )
{
@ -129,16 +141,17 @@ if ( !$server_up )
Info( "Starting control server $id/$protocol" );
close( CLIENT );
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol");
}
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol");
}
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: $!" );
socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
or die( "Can't open socket: $!" );
my $attempts = 0;
while (!connect( CLIENT, $saddr ))
{
@ -156,7 +169,9 @@ if ( !$server_up )
logReinit();
Info( "Control server $id/$protocol starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) );
Info( "Control server $id/$protocol starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
$0 = $0." --id $id";
@ -166,7 +181,8 @@ if ( !$server_up )
$control->open();
socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" );
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: $!" );
@ -193,9 +209,9 @@ if ( !$server_up )
my $command = $params->{command};
close( CLIENT );
if ( $command eq 'quit' ) {
last;
}
if ( $command eq 'quit' ) {
last;
}
$control->$command( $params );
}
else
@ -220,7 +236,9 @@ if ( !$server_up )
last;
}
}
Info( "Control server $id/$protocol exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) );
Info( "Control server $id/$protocol exiting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
unlink( $sock_file );
$control->close();
exit( 0 );

View File

@ -20,13 +20,30 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script is the gateway for controlling the various ZoneMinder
# daemons. All starting, stopping and restarting goes through here.
# On the first invocation it starts up a server which subsequently
# records what's running and what's not. Other invocations just
# connect to the server and pass instructions to it.
#
=head1 NAME
zmdc.pl - ZoneMinder Daemon Control script
=head1 SYNOPSIS
zmdc.pl {command} [daemon [options]]
=head1 DESCRIPTION
This script is the gateway for controlling the various ZoneMinder
daemons. All starting, stopping and restarting goes through here.
On the first invocation it starts up a server which subsequently
records what's running and what's not. Other invocations just
connect to the server and pass instructions to it.
=head1 OPTIONS
{command} - One of 'startup|shutdown|status|check|logrot' or
'start|stop|restart|reload|version'.
[daemon [options]] - Daemon name and options, required for second group of commands
=cut
use strict;
use bytes;
@ -49,6 +66,7 @@ use ZoneMinder;
use POSIX;
use Socket;
use IO::Handle;
use autouse 'Pod::Usage'=>qw(pod2usage);
#use Data::Dumper;
use constant SOCK_FILE => $Config{ZM_PATH_SOCKS}.'/zmdc.sock';
@ -72,34 +90,22 @@ my @daemons = (
'zmtrack.pl'
);
sub Usage
{
print( "
Usage: zmdc.pl <command> [daemon [options]]
Parameters are :-
<command> - One of 'startup|shutdown|status|check|logrot' or
'start|stop|restart|reload|version'.
[daemon [options]] - Daemon name and options, required for second group of commands
");
exit( -1 );
}
my $command = shift @ARGV;
if( !$command )
{
print( STDERR "No command given\n" );
Usage();
pod2usage(-exitstatus => -1);
}
if ( $command eq 'version' ) {
print ZoneMinder::Base::ZM_VERSION."\n";
exit( 0 );
print ZoneMinder::Base::ZM_VERSION."\n";
exit( 0 );
}
my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/;
my $daemon = shift( @ARGV );
if( $needs_daemon && !$daemon )
{
print( STDERR "No daemon given\n" );
Usage();
pod2usage(-exitstatus => -1);
}
my @args;
@ -113,7 +119,7 @@ if ( $needs_daemon )
else
{
print( STDERR "Invalid daemon '$daemon' specified" );
Usage();
pod2usage(-exitstatus => -1);
}
}
@ -152,7 +158,7 @@ if ( !$server_up )
print( "Unable to connect to server\n" );
exit( -1 );
}
# The server isn't there
# The server isn't there
print( "Starting server\n" );
close( CLIENT );
@ -235,12 +241,15 @@ sub run
logInit();
dPrint( ZoneMinder::Logger::INFO, "Server starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "Server starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
if ( open( PID, ">".ZM_PID ) )
if ( open( my $PID, '>', ZM_PID ) )
{
print( PID $$ );
close( PID );
print( $PID $$ );
close( $PID );
}
killAll( 1 );
@ -354,7 +363,10 @@ sub run
restartPending();
}
}
dPrint( ZoneMinder::Logger::INFO, "Server exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "Server exiting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
unlink( main::SOCK_FILE );
unlink( ZM_PID );
exit();
@ -413,7 +425,10 @@ sub start
}
elsif ( $process->{pid} && $pid_hash{$process->{pid}} )
{
dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" );
dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) )
.", pid = $process->{pid}\n"
);
return();
}
@ -428,7 +443,10 @@ sub start
$process->{started} = time();
delete( $process->{pending} );
dPrint( ZoneMinder::Logger::INFO, "'$command' starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" );
dPrint( ZoneMinder::Logger::INFO, "'$command' starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) )
.", pid = $process->{pid}\n"
);
$cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process;
sigprocmask( SIG_SETMASK, $sigset ) or Fatal( "Can't restore SIGCHLD: $!" );
@ -437,7 +455,11 @@ sub start
{
logReinit();
dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) )."' started at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) )
."' started at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
if ( $daemon =~ /^${daemon_patt}$/ )
{
@ -501,7 +523,10 @@ sub _stop
elsif ( $process->{pending} )
{
delete( $cmd_hash{$command} );
dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
return();
}
@ -512,7 +537,11 @@ sub _stop
return();
}
dPrint( ZoneMinder::Logger::INFO, "'$daemon ".join( ' ', @args )."' stopping at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "'$daemon ".join( ' ', @args )
."' stopping at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
$process->{keepalive} = !$final;
kill( 'TERM', $cpid );
delete( $cmd_hash{$command} );
@ -694,7 +723,10 @@ sub shutdownAll
stop( $process->{daemon}, @{$process->{args}} );
}
killAll( 5 );
dPrint( ZoneMinder::Logger::INFO, "Server shutdown at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
dPrint( ZoneMinder::Logger::INFO, "Server shutdown at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
unlink( main::SOCK_FILE );
unlink( ZM_PID );
close( CLIENT );
@ -750,7 +782,10 @@ sub status
if ( $process->{pending} )
{
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" );
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )
."\n"
);
}
else
{
@ -761,13 +796,19 @@ sub status
return();
}
}
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}" );
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) )
.", pid = $process->{pid}"
);
}
else
{
foreach my $process ( values(%pid_hash) )
{
my $out_str = "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}";
my $out_str = "'$process->{command}' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) )
.", pid = $process->{pid}"
;
$out_str .= ", valid" if ( kill( 0, $process->{pid} ) );
$out_str .= "\n";
dPrint( ZoneMinder::Logger::DEBUG, $out_str );
@ -776,7 +817,10 @@ sub status
{
if ( $process->{pending} )
{
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" );
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )
."\n"
);
}
}
}
@ -786,19 +830,23 @@ sub killAll
{
my $delay = shift;
sleep( $delay );
my $killall;
if ( '@HOST_OS@' eq 'BSD' ) {
$killall = 'killall -';
} else {
$killall = 'killall -q -s ';
}
foreach my $daemon ( @daemons ) {
my $killall;
if ( '@HOST_OS@' eq 'BSD' )
{
$killall = 'killall -';
} elsif ( '@HOST_OS@' eq 'solaris' ) {
$killall = 'pkill -';
} else {
$killall = 'killall -q -s ';
}
foreach my $daemon ( @daemons )
{
my $cmd = $killall ."TERM $daemon";
Debug( $cmd );
qx( $cmd );
}
sleep( $delay );
my $cmd = $killall ."TERM $daemon";
Debug( $cmd );
qx( $cmd );
}
sleep( $delay );
foreach my $daemon ( @daemons )
{
my $cmd = $killall."KILL $daemon";

View File

@ -20,11 +20,27 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script continuously monitors the recorded events for the given
# monitor and applies any filters which would delete and/or upload
# matching events
#
=head1 NAME
zmfilter.pl - ZoneMinder tool to filter events
=head1 SYNOPSIS
zmfilter.pl [-f <filter name>,--filter=<filter name>] | -v, --version
=head1 DESCRIPTION
This script continuously monitors the recorded events for the given
monitor and applies any filters which would delete and/or upload
matching events.
=head1 OPTIONS
-f{filter name}, --filter={filter name} - The name of a specific filter to run
-v, --version - Print ZoneMinder version
=cut
use strict;
use bytes;
@ -49,9 +65,13 @@ use POSIX;
use Time::HiRes qw/gettimeofday/;
use Date::Manip;
use Getopt::Long;
use Data::Dumper;
use autouse 'Pod::Usage'=>qw(pod2usage);
use autouse 'Data::Dumper'=>qw(Dumper);
use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS});
use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
? $Config{ZM_DIR_EVENTS}
: ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
;
logInit();
logSetSignal();
@ -105,7 +125,6 @@ if ( $Config{ZM_OPT_MESSAGE} )
}
}
$| = 1;
$ENV{PATH} = '/bin:/usr/bin';
@ -117,16 +136,6 @@ my $event_id = 0;
my $filter_parm = "";
my $version = 0;
sub Usage
{
print( "
Usage: zmfilter.pl [-f <filter name>,--filter=<filter name>] | -v, --version
Parameters are :-
-f<filter name>, --filter=<filter name> - The name of a specific filter to run
");
exit( -1 );
}
#
# More or less replicates the equivalent PHP function
#
@ -159,13 +168,14 @@ sub DateTimeToSQL
return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) );
}
if ( !GetOptions( 'filter=s'=>\$filter_parm, version=>\$version ) )
{
Usage();
}
GetOptions(
'filter=s' =>\$filter_parm,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
if ( $version ) {
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
}
if ( ! EVENT_PATH ) {
@ -196,7 +206,7 @@ my $last_action = 0;
while( 1 )
{
my $now = time;
my $now = time;
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} )
{
Debug( "Reloading filters\n" );
@ -257,7 +267,7 @@ sub getFilters
my $filter_name = shift;
my @filters;
my $sql = "select * from Filters where";
my $sql = "SELECT * FROM Filters WHERE";
if ( $filter_name )
{
$sql .= " Name = ? and";
@ -266,22 +276,56 @@ sub getFilters
{
$sql .= " Background = 1 and";
}
$sql .= " (AutoArchive = 1 or AutoVideo = 1 or AutoUpload = 1 or AutoEmail = 1 or AutoMessage = 1 or AutoExecute = 1 or AutoDelete = 1) order by Name";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
$sql .= "( AutoArchive = 1
or AutoVideo = 1
or AutoUpload = 1
or AutoEmail = 1
or AutoMessage = 1
or AutoExecute = 1
or AutoDelete = 1
) ORDER BY Name";
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res;
if ( $filter_name )
{
$res = $sth->execute( $filter_name ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$res = $sth->execute( $filter_name )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
}
else
{
$res = $sth->execute() or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$res = $sth->execute()
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
}
FILTER: while( my $db_filter = $sth->fetchrow_hashref() )
{
Debug( "Found filter '$db_filter->{Name}'\n" );
my $filter_expr = jsonDecode( $db_filter->{Query} );
my $sql = "select E.Id,E.MonitorId,M.Name as MonitorName,M.DefaultRate,M.DefaultScale,E.Name,E.Cause,E.Notes,E.StartTime,unix_timestamp(E.StartTime) as Time,E.Length,E.Frames,E.AlarmFrames,E.TotScore,E.AvgScore,E.MaxScore,E.Archived,E.Videoed,E.Uploaded,E.Emailed,E.Messaged,E.Executed from Events as E inner join Monitors as M on M.Id = E.MonitorId";
my $sql = "SELECT E.Id,
E.MonitorId,
M.Name as MonitorName,
M.DefaultRate,
M.DefaultScale,
E.Name,
E.Cause,
E.Notes,
E.StartTime,
unix_timestamp(E.StartTime) as Time,
E.Length,
E.Frames,
E.AlarmFrames,
E.TotScore,
E.AvgScore,
E.MaxScore,
E.Archived,
E.Videoed,
E.Uploaded,
E.Emailed,
E.Messaged,
E.Executed
FROM Events as E
INNER JOIN Monitors as M on M.Id = E.MonitorId
";
$db_filter->{Sql} = '';
if ( $filter_expr->{terms} )
@ -348,7 +392,10 @@ sub getFilters
{
$value = "'$temp_value'";
}
elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' || $filter_expr->{terms}[$i]->{attr} eq 'Cause' || $filter_expr->{terms}[$i]->{attr} eq 'Notes' )
elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name'
|| $filter_expr->{terms}[$i]->{attr} eq 'Cause'
|| $filter_expr->{terms}[$i]->{attr} eq 'Notes'
)
{
$value = "'$temp_value'";
}
@ -357,7 +404,8 @@ sub getFilters
$value = DateTimeToSQL( $temp_value );
if ( !$value )
{
Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" );
Error( "Error parsing date/time '$temp_value', "
."skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
$value = "'$value'";
@ -367,7 +415,8 @@ sub getFilters
$value = DateTimeToSQL( $temp_value );
if ( !$value )
{
Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" );
Error( "Error parsing date/time '$temp_value', "
."skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
$value = "to_days( '$value' )";
@ -377,7 +426,8 @@ sub getFilters
$value = DateTimeToSQL( $temp_value );
if ( !$value )
{
Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" );
Error( "Error parsing date/time '$temp_value', "
."skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
$value = "extract( hour_second from '$value' )";
@ -469,7 +519,7 @@ sub getFilters
my $sort_column = '';
if ( $filter_expr->{sort_field} eq 'Id' )
{
$sort_column = "E.Id";
$sort_column = "E.Id";
}
elsif ( $filter_expr->{sort_field} eq 'MonitorName' )
{
@ -525,13 +575,15 @@ sub getFilters
$script =~ s/\s.*$//;
if ( !-e $script )
{
Error( "Auto execute script '$script' not found, skipping filter '$db_filter->{Name}'\n" );
Error( "Auto execute script '$script' not found, "
."skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
elsif ( !-x $script )
{
Error( "Auto execute script '$script' not executable, skipping filter '$db_filter->{Name}'\n" );
Error( "Auto execute script '$script' not executable, "
."skipping filter '$db_filter->{Name}'\n" );
next FILTER;
}
}
@ -557,7 +609,7 @@ sub checkFilter
"\n"
);
my $sql = $filter->{Sql};
if ( $filter->{HasDiskPercent} )
{
my $disk_percent = getDiskPercent();
@ -574,7 +626,8 @@ sub checkFilter
$sql =~ s/zmSystemLoad/$load/g;
}
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute();
if ( !$res )
{
@ -591,8 +644,10 @@ sub checkFilter
Info( "Archiving event $event->{Id}\n" );
# Do it individually to avoid locking up the table for new events
my $sql = "update Events set Archived = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
}
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} )
{
@ -636,18 +691,24 @@ sub checkFilter
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId}\n" );
# Do it individually to avoid locking up the table for new events
my $sql = "delete from Events where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
if ( ! $Config{ZM_OPT_FAST_DELETE} )
{
my $sql = "delete from Frames where EventId = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$sql = "delete from Stats where EventId = ?";
$sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
$res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
$res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
deleteEventFiles( $event->{Id}, $event->{MonitorId} );
}
@ -699,7 +760,8 @@ sub generateVideo
$format = $ffmpeg_formats[0];
}
my $command = $Config{ZM_PATH_BIN}."/zmvideo.pl -e ".$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format;
my $command = $Config{ZM_PATH_BIN}."/zmvideo.pl -e "
.$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format;
my $output = qx($command);
chomp( $output );
my $status = $? >> 8;
@ -719,11 +781,13 @@ sub generateVideo
else
{
my $sql = "update Events set Videoed = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
if ( wantarray() )
{
return( $format, sprintf( "%s/%s", getEventPath( $event ), $output ) );
return( $format, sprintf( "%s/%s", getEventPath( $event ), $output ) );
}
}
return( 1 );
@ -741,7 +805,15 @@ sub uploadArchFile
}
my $archFile = $event->{MonitorName}.'-'.$event->{Id};
my $archImagePath = getEventPath( $event )."/".(( $Config{ZM_UPLOAD_ARCH_ANALYSE} )?'{*analyse,*capture}':'*capture').".jpg";
my $archImagePath = getEventPath( $event )
."/"
.(
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
? '{*analyse,*capture}'
: '*capture'
)
.".jpg"
;
my @archImageFiles = glob($archImagePath);
my $archLocPath;
@ -764,7 +836,10 @@ sub uploadArchFile
$archError = 1;
last;
}
$member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS} ? &COMPRESSION_DEFLATED : &COMPRESSION_STORED );
$member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS}
? &COMPRESSION_DEFLATED
: &COMPRESSION_STORED
);
}
if ( !$archError )
{
@ -793,7 +868,12 @@ sub uploadArchFile
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" );
if ( $archError = !Archive::Tar->create_archive( $archLocPath, $Config{ZM_UPLOAD_ARCH_COMPRESS}, @archImageFiles ) )
if ( $archError = !Archive::Tar->create_archive(
$archLocPath,
$Config{ZM_UPLOAD_ARCH_COMPRESS},
@archImageFiles
)
)
{
Error( "Tar error: ".Archive::Tar->error()."\n " );
}
@ -808,42 +888,63 @@ sub uploadArchFile
if ( $Config{ZM_UPLOAD_PROTOCOL} eq "ftp" )
{
Info( "Uploading to ".$Config{ZM_UPLOAD_HOST}." using FTP\n" );
my $ftp = Net::FTP->new( $Config{ZM_UPLOAD_HOST}, Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE}, Debug=>$Config{ZM_UPLOAD_DEBUG} );
my $ftp = Net::FTP->new(
$Config{ZM_UPLOAD_HOST},
Timeout=>$Config{ZM_UPLOAD_TIMEOUT},
Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE},
Debug=>$Config{ZM_UPLOAD_DEBUG}
);
if ( !$ftp )
{
Error( "Can't create FTP connection: $@" );
return( 0 );
}
$ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) or Error( "FTP - Can't login" );
$ftp->binary() or Error( "FTP - Can't go binary" );
$ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) or Error( "FTP - Can't cwd" ) if ( $Config{ZM_UPLOAD_REM_DIR} );
$ftp->put( $archLocPath ) or Error( "FTP - Can't upload '$archLocPath'" );
$ftp->quit() or Error( "FTP - Can't quit" );
$ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} )
or Error( "FTP - Can't login" );
$ftp->binary()
or Error( "FTP - Can't go binary" );
$ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} )
or Error( "FTP - Can't cwd" )
if ( $Config{ZM_UPLOAD_REM_DIR} );
$ftp->put( $archLocPath )
or Error( "FTP - Can't upload '$archLocPath'" );
$ftp->quit()
or Error( "FTP - Can't quit" );
}
else
{
my $host = $Config{ZM_UPLOAD_HOST};
$host .= ":".$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT};
$host .= ":".$Config{ZM_UPLOAD_PORT}
if $Config{ZM_UPLOAD_PORT};
Info( "Uploading to ".$host." using SFTP\n" );
my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} );
$sftpOptions{password} = $Config{ZM_UPLOAD_PASS} if $Config{ZM_UPLOAD_PASS};
$sftpOptions{port} = $Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT};
$sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT} if $Config{ZM_UPLOAD_TIMEOUT};
$sftpOptions{password} = $Config{ZM_UPLOAD_PASS}
if $Config{ZM_UPLOAD_PASS};
$sftpOptions{port} = $Config{ZM_UPLOAD_PORT}
if $Config{ZM_UPLOAD_PORT};
$sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT}
if $Config{ZM_UPLOAD_TIMEOUT};
$sftpOptions{more} = [ '-o'=>'StrictHostKeyChecking=no' ];
$Net::SFTP::Foreign::debug = -1 if $Config{ZM_UPLOAD_DEBUG};
$Net::SFTP::Foreign::debug = -1
if $Config{ZM_UPLOAD_DEBUG};
my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions );
if ( $sftp->error )
{
Error( "Can't create SFTP connection: ".$sftp->error );
return( 0 );
}
$sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) or Error( "SFTP - Can't setcwd: ".$sftp->error ) if $Config{ZM_UPLOAD_REM_DIR};
$sftp->put( $archLocPath, $archFile ) or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error );
$sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} )
or Error( "SFTP - Can't setcwd: ".$sftp->error )
if $Config{ZM_UPLOAD_REM_DIR};
$sftp->put( $archLocPath, $archFile )
or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error );
}
unlink( $archLocPath );
my $sql = "update Events set Uploaded = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
}
return( 1 );
}
@ -864,9 +965,28 @@ sub substituteTags
if ( $need_monitor )
{
my $db_now = strftime( "%Y-%m-%d %H:%M:%S", localtime() );
my $sql = "select M.Id, count(E.Id) as EventCount, count(if(E.Archived,1,NULL)) as ArchEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) as HourEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) as DayEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) as WeekEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) as MonthEventCount from Monitors as M left join Events as E on E.MonitorId = M.Id where MonitorId = ? group by E.MonitorId order by Id";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sql = "SELECT
M.Id,
count(E.Id) as EventCount,
count(if(E.Archived,1,NULL))
as ArchEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL))
as HourEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL))
as DayEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL))
as WeekEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL))
as MonthEventCount
FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id
WHERE MonitorId = ?
GROUP BY E.MonitorId
ORDER BY Id"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{MonitorId} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$monitor = $sth->fetchrow_hashref();
$sth->finish();
return() if ( !$monitor );
@ -879,9 +999,14 @@ sub substituteTags
my $max_alarm_score = 0;
if ( $need_images )
{
my $sql = "select * from Frames where EventId = ? and Type = 'Alarm' order by FrameId";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sql = "SELECT * FROM Frames
WHERE EventId = ? AND Type = 'Alarm'
ORDER BY FrameId"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
while( my $frame = $sth->fetchrow_hashref() )
{
if ( !$first_alarm_frame )
@ -929,14 +1054,34 @@ sub substituteTags
$text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
if ( $attachments_ref && $text =~ s/%EI1%//g )
{
push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath( $event ), $first_alarm_frame->{FrameId} ) } );
push( @$attachments_ref,
{
type=>"image/jpeg",
path=>sprintf(
"%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg",
getEventPath( $event ),
$first_alarm_frame->{FrameId}
)
}
);
}
if ( $attachments_ref && $text =~ s/%EIM%//g )
{
# Don't attach the same image twice
if ( !@$attachments_ref || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) )
if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
)
{
push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath( $event ), $max_alarm_frame->{FrameId} ) } );
push( @$attachments_ref,
{
type=>"image/jpeg",
path=>sprintf(
"%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg",
getEventPath( $event ),
$max_alarm_frame->{FrameId}
)
}
);
}
}
}
@ -964,7 +1109,7 @@ sub substituteTags
$text =~ s/%FN%/$filter->{Name}/g;
( my $filter_name = $filter->{Name} ) =~ s/ /+/g;
$text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g;
return( $text );
}
@ -1023,7 +1168,7 @@ sub sendEmail
### Send the Message
MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 );
$mail->send();
}
}
else
{
my $mail = MIME::Entity->build(
@ -1056,8 +1201,10 @@ sub sendEmail
Info( "Notification email sent\n" );
}
my $sql = "update Events set Emailed = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
return( 1 );
}
@ -1117,7 +1264,7 @@ sub sendMessage
### Send the Message
MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 );
$mail->send();
}
}
else
{
my $mail = MIME::Entity->build(
@ -1137,7 +1284,9 @@ sub sendMessage
Encoding => "base64"
);
}
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} );
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST},
MailFrom => $Config{ZM_FROM_EMAIL}
);
}
};
if ( $@ )
@ -1150,8 +1299,10 @@ sub sendMessage
Info( "Notification message sent\n" );
}
my $sql = "update Events set Messaged = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
return( 1 );
}
@ -1182,8 +1333,10 @@ sub executeCommand
else
{
my $sql = "update Events set Executed = 1 where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
}
return( 1 );
}

View File

@ -20,10 +20,21 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script is used to start and stop the ZoneMinder package primarily to
# allow command line control for automatic restart on reboot (see zm script)
#
=head1 NAME
zmpkg.pl - ZoneMinder Package Control Script
=head1 SYNOPSIS
zmpkg.pl {start|stop|restart|status|logrot|'state'|version}
=head1 DESCRIPTION
This script is used to start and stop the ZoneMinder package primarily to
allow command line control for automatic restart on reboot (see zm script)
=cut
use strict;
use bytes;
@ -38,6 +49,7 @@ use ZoneMinder;
use DBI;
use POSIX;
use Time::HiRes qw/gettimeofday/;
use autouse 'Pod::Usage'=>qw(pod2usage);
# Detaint our environment
$ENV{PATH} = '/bin:/usr/bin';
@ -46,10 +58,10 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
my $command = $ARGV[0];
my $command = $ARGV[0]||'';
if ( $command eq 'version' ) {
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
}
my $state;
@ -58,39 +70,43 @@ my $dbh;
if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot|version)$/ )
{
if ( $command )
{
$dbh = zmDbConnect();
# Check to see if it's a valid run state
my $sql = 'select * from States where Name = ?';
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $command ) or Fatal( "Can't execute: ".$sth->errstr() );
if ( $state = $sth->fetchrow_hashref() )
{
$state->{Name} = $command;
$state->{Definitions} = [];
foreach( split( /,/, $state->{Definition} ) )
{
my ( $id, $function, $enabled ) = split( /:/, $_ );
push( @{$state->{Definitions}}, { Id=>$id, Function=>$function, Enabled=>$enabled } );
}
$command = 'state';
}
else
{
$command = undef;
}
}
if ( !$command )
{
print( "Usage: zmpkg.pl <start|stop|restart|status|logrot|'state'|version>\n" );
exit( -1 );
}
if ( $command )
{
$dbh = zmDbConnect();
# Check to see if it's a valid run state
my $sql = 'select * from States where Name = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $command )
or Fatal( "Can't execute: ".$sth->errstr() );
if ( $state = $sth->fetchrow_hashref() )
{
$state->{Name} = $command;
$state->{Definitions} = [];
foreach( split( /,/, $state->{Definition} ) )
{
my ( $id, $function, $enabled ) = split( /:/, $_ );
push( @{$state->{Definitions}},
{ Id=>$id, Function=>$function, Enabled=>$enabled }
);
}
$command = 'state';
}
else
{
$command = undef;
}
}
if ( !$command )
{
pod2usage(-exitstatus => -1);
}
}
$dbh = zmDbConnect() if ! $dbh;
# Move to the right place
chdir( $Config{ZM_PATH_WEB} ) or Fatal( "Can't chdir to '".$Config{ZM_PATH_WEB}."': $!" );
chdir( $Config{ZM_PATH_WEB} )
or Fatal( "Can't chdir to '".$Config{ZM_PATH_WEB}."': $!" );
my $dbg_id = "";
@ -100,209 +116,232 @@ my $retval = 0;
if ( $command eq "state" )
{
Info( "Updating DB: $state->{Name}\n" );
my $sql = "select * from Monitors order by Id asc";
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() )
{
foreach my $definition ( @{$state->{Definitions}} )
{
if ( $monitor->{Id} =~ /^$definition->{Id}$/ )
{
$monitor->{NewFunction} = $definition->{Function};
$monitor->{NewEnabled} = $definition->{Enabled};
}
}
#next if ( !$monitor->{NewFunction} );
$monitor->{NewFunction} = 'None' if ( !$monitor->{NewFunction} );
$monitor->{NewEnabled} = 0 if ( !$monitor->{NewEnabled} );
if ( $monitor->{Function} ne $monitor->{NewFunction} || $monitor->{Enabled} ne $monitor->{NewEnabled} )
{
my $sql = "update Monitors set Function = ?, Enabled = ? where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} ) or Fatal( "Can't execute: ".$sth->errstr() );
}
}
$sth->finish();
Info( "Updating DB: $state->{Name}\n" );
my $sql = "select * from Monitors order by Id asc";
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() )
{
foreach my $definition ( @{$state->{Definitions}} )
{
if ( $monitor->{Id} =~ /^$definition->{Id}$/ )
{
$monitor->{NewFunction} = $definition->{Function};
$monitor->{NewEnabled} = $definition->{Enabled};
}
}
#next if ( !$monitor->{NewFunction} );
$monitor->{NewFunction} = 'None'
if ( !$monitor->{NewFunction} );
$monitor->{NewEnabled} = 0
if ( !$monitor->{NewEnabled} );
if ( $monitor->{Function} ne $monitor->{NewFunction}
|| $monitor->{Enabled} ne $monitor->{NewEnabled}
)
{
my $sql = "update Monitors set Function = ?, Enabled = ? where Id = ?";
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} )
or Fatal( "Can't execute: ".$sth->errstr() );
}
}
$sth->finish();
$command = "restart";
$command = "restart";
}
# Check if we are running systemd and if we have been called by the system
if ( $command =~ /^(start|stop|restart)$/ )
{
# We have to detaint to keep perl from complaining
$command = $1;
# We have to detaint to keep perl from complaining
$command = $1;
if ( systemdRunning() && !calledBysystem() ) {
qx(@BINDIR@/zmsystemctl.pl $command);
$command = "";
}
if ( systemdRunning() && !calledBysystem() ) {
qx(@BINDIR@/zmsystemctl.pl $command);
$command = "";
}
}
if ( $command =~ /^(?:stop|restart)$/ )
{
my $status = runCommand( "zmdc.pl check" );
my $status = runCommand( "zmdc.pl check" );
if ( $status eq "running" )
{
runCommand( "zmdc.pl shutdown" );
zmMemTidy();
}
else
{
$retval = 1;
}
if ( $status eq "running" )
{
runCommand( "zmdc.pl shutdown" );
zmMemTidy();
}
else
{
$retval = 1;
}
}
#runCommand( "zmupdate.pl -f" );
if ( $command =~ /^(?:start|restart)$/ )
{
my $status = runCommand( "zmdc.pl check" );
my $status = runCommand( "zmdc.pl check" );
if ( $status eq "stopped" )
{
if ( $Config{ZM_DYN_DB_VERSION} and ( $Config{ZM_DYN_DB_VERSION} ne ZM_VERSION ) )
if ( $status eq "stopped" )
{
if ( $Config{ZM_DYN_DB_VERSION}
and ( $Config{ZM_DYN_DB_VERSION} ne ZM_VERSION )
)
{
Fatal( "Version mismatch, system is version ".ZM_VERSION.", database is ".$Config{ZM_DYN_DB_VERSION}.", please run zmupdate.pl to update." );
Fatal( "Version mismatch, system is version ".ZM_VERSION
.", database is ".$Config{ZM_DYN_DB_VERSION}
.", please run zmupdate.pl to update."
);
exit( -1 );
}
# Recreate the temporary directory if it's been wiped
verifyFolder("@ZM_TMPDIR@");
verifyFolder("@ZM_TMPDIR@");
# Recreate the run directory if it's been wiped
verifyFolder("@ZM_RUNDIR@");
verifyFolder("@ZM_RUNDIR@");
# Recreate the sock directory if it's been wiped
verifyFolder("@ZM_SOCKDIR@");
verifyFolder("@ZM_SOCKDIR@");
zmMemTidy();
runCommand( "zmdc.pl startup" );
zmMemTidy();
runCommand( "zmdc.pl startup" );
my $sql = "select * from Monitors";
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() )
{
if ( $monitor->{Function} ne 'None' )
{
if ( $monitor->{Type} eq 'Local' )
{
runCommand( "zmdc.pl start zmc -d $monitor->{Device}" );
}
else
{
runCommand( "zmdc.pl start zmc -m $monitor->{Id}" );
}
if ( $monitor->{Function} ne 'Monitor' )
{
if ( $Config{ZM_OPT_FRAME_SERVER} )
{
runCommand( "zmdc.pl start zmf -m $monitor->{Id}" );
}
runCommand( "zmdc.pl start zma -m $monitor->{Id}" );
}
if ( $Config{ZM_OPT_CONTROL} )
{
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' )
{
if ( $monitor->{Controllable} && $monitor->{TrackMotion} )
{
runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" );
}
}
}
}
}
$sth->finish();
my $sql = "select * from Monitors";
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() )
{
if ( $monitor->{Function} ne 'None' )
{
if ( $monitor->{Type} eq 'Local' )
{
runCommand( "zmdc.pl start zmc -d $monitor->{Device}" );
}
else
{
runCommand( "zmdc.pl start zmc -m $monitor->{Id}" );
}
if ( $monitor->{Function} ne 'Monitor' )
{
if ( $Config{ZM_OPT_FRAME_SERVER} )
{
runCommand( "zmdc.pl start zmf -m $monitor->{Id}" );
}
runCommand( "zmdc.pl start zma -m $monitor->{Id}" );
}
if ( $Config{ZM_OPT_CONTROL} )
{
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' )
{
if ( $monitor->{Controllable} && $monitor->{TrackMotion} )
{
runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" );
}
}
}
}
}
$sth->finish();
# This is now started unconditionally
runCommand( "zmdc.pl start zmfilter.pl" );
if ( $Config{ZM_RUN_AUDIT} )
{
runCommand( "zmdc.pl start zmaudit.pl -c" );
}
if ( $Config{ZM_OPT_TRIGGERS} )
{
runCommand( "zmdc.pl start zmtrigger.pl" );
}
if ( $Config{ZM_OPT_X10} )
{
runCommand( "zmdc.pl start zmx10.pl -c start" );
}
runCommand( "zmdc.pl start zmwatch.pl" );
if ( $Config{ZM_CHECK_FOR_UPDATES} )
{
runCommand( "zmdc.pl start zmupdate.pl -c" );
}
}
else
{
$retval = 1;
}
# This is now started unconditionally
runCommand( "zmdc.pl start zmfilter.pl" );
if ( $Config{ZM_RUN_AUDIT} )
{
runCommand( "zmdc.pl start zmaudit.pl -c" );
}
if ( $Config{ZM_OPT_TRIGGERS} )
{
runCommand( "zmdc.pl start zmtrigger.pl" );
}
if ( $Config{ZM_OPT_X10} )
{
runCommand( "zmdc.pl start zmx10.pl -c start" );
}
runCommand( "zmdc.pl start zmwatch.pl" );
if ( $Config{ZM_CHECK_FOR_UPDATES} )
{
runCommand( "zmdc.pl start zmupdate.pl -c" );
}
}
else
{
$retval = 1;
}
}
if ( $command eq "status" )
{
my $status = runCommand( "zmdc.pl check" );
my $status = runCommand( "zmdc.pl check" );
print( STDOUT $status."\n" );
print( STDOUT $status."\n" );
}
if ( $command eq "logrot" )
{
runCommand( "zmdc.pl logrot" );
runCommand( "zmdc.pl logrot" );
}
exit( $retval );
sub systemdRunning
{
my $result = 0;
my $result = 0;
my $output = qx(ps -o comm="" 1);
chomp( $output );
my $output = qx(ps -o comm="" -p 1);
chomp( $output );
if ($output =~ /systemd/) {
$result = 1;
}
if ($output =~ /systemd/) {
$result = 1;
}
return $result;
return $result;
}
sub calledBysystem
{
my $result = 0;
my $ppid = getppid();
my $result = 0;
my $ppid = getppid();
my $output = qx(ps -o comm="" $ppid);
chomp( $output );
my $output = qx(ps -o comm="" -p $ppid);
chomp( $output );
if ($output =~ /^(?:systemd|init)$/) {
$result = 1;
}
if ($output =~ /^(?:systemd|init)$/) {
$result = 1;
}
return $result;
return $result;
}
sub verifyFolder
{
my $folder = shift;
my $folder = shift;
# Recreate the temporary directory if it's been wiped
if ( !-e $folder )
{
Debug( "Recreating directory '$folder'" );
mkdir( "$folder", 0774 ) or Fatal( "Can't create missing temporary directory '$folder': $!" );
mkdir( "$folder", 0774 )
or Fatal( "Can't create missing temporary directory '$folder': $!" );
my ( $runName ) = getpwuid( $> );
if ( $runName ne $Config{ZM_WEB_USER} )
{
# Not running as web user, so should be root in which case chown the directory
my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( $Config{ZM_WEB_USER} ) or Fatal( "Can't get user details for web user '".$Config{ZM_WEB_USER}."': $!" );
chown( $webUid, $webGid, "$folder" ) or Fatal( "Can't change ownership of directory '$folder' to '".$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" );
# Not running as web user, so should be root in which case
# chown the directory
my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( $Config{ZM_WEB_USER} )
or Fatal( "Can't get user details for web user '"
.$Config{ZM_WEB_USER}."': $!"
);
chown( $webUid, $webGid, "$folder" )
or Fatal( "Can't change ownership of directory '$folder' to '"
.$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!"
);
}
}
}

View File

@ -2,7 +2,7 @@
#
# ==========================================================================
#
# ZoneMinder Update Script, $Date$, $Revision$
# ZoneMinder systemctl wrapper, $Date$, $Revision$
# Copyright (C) 2001-2008 Philip Coombes
#
# This program is free software; you can redistribute it and/or
@ -20,25 +20,39 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This is a wrapper script that allows zoneminder to start and stop itself
# in a manner that keeps it in-sync with systemd. This script is intended
# to be called internally by zoneminder and may not give the desired results
# if run from the command line.
#
=head1 NAME
zmsystemctl.pl - ZoneMinder systemctl wrapper
=head1 SYNOPSIS
zmsystemctl.pl {start|stop|restart|version}
=head1 DESCRIPTION
This is a wrapper script that allows zoneminder to start and stop itself
in a manner that keeps it in-sync with systemd. This script is intended
to be called internally by zoneminder and may not give the desired results
if run from the command line.
=cut
use warnings;
use strict;
use bytes;
use autouse 'Pod::Usage'=>qw(pod2usage);
@EXTRA_PERL_LIB@
use ZoneMinder::Logger qw(:all);
my $command = $ARGV[0];
if ( (scalar(@ARGV) == 1) && ($command =~ /^(start|stop|restart|version)$/ )) {
$command = $1;
if ( (scalar(@ARGV) == 1)
&& ($command =~ /^(start|stop|restart|version)$/ )
){
$command = $1;
} else {
die(" USAGE: zmsystemctl.pl <start|stop|restart|version>\n");
pod2usage(-exitstatus => -1);
}
my $path = qx(which systemctl);
@ -46,7 +60,7 @@ chomp($path);
my $status = $? >> 8;
if ( !$path || $status ) {
Fatal( "Unable to determine systemctl executable. Is systemd in use?" );
Fatal( "Unable to determine systemctl executable. Is systemd in use?" );
}
Info( "Redirecting command through systemctl\n" );

View File

@ -20,10 +20,26 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script is used to trigger and cancel alarms from external sources
# using an arbitrary text based format
#
=head1 NAME
zmtrack.pl - ZoneMinder Experimental PTZ Tracking Script
=head1 SYNOPSIS
zmtrack.pl -m <monitor>
zmtrack.pl --monitor=<monitor>
=head1 OPTIONS
-m<monitor>, --monitor=<monitor> - Id of the monitor to track
=head1 DESCRIPTION
This script is used to trigger and cancel alarms from external sources
using an arbitrary text based format.
=cut
use strict;
use bytes;
@ -45,8 +61,9 @@ use constant SLEEP_TIME => 10000; # In microseconds
use ZoneMinder;
use DBI;
use POSIX;
use Data::Dumper;
use autouse 'Data::Dumper'=>qw(Dumper);
use Getopt::Long;
use autouse 'Pod::Usage'=>qw(pod2usage);
use Time::HiRes qw( usleep );
$| = 1;
@ -57,20 +74,8 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $mid = 0;
sub Usage
{
print( "
Usage: zmtrack.pl -m <monitor>,--monitor=<monitor>]
Parameters are :-
-m<monitor>, --monitor=<monitor> - Id of the monitor to track
");
exit( -1 );
}
if ( !GetOptions( 'monitor=s'=>\$mid ) )
{
Usage();
}
GetOptions( 'monitor=s'=>\$mid )
or pod2usage(-exitstatus => -1);
logInit();
logSetSignal();
@ -78,44 +83,52 @@ logSetSignal();
my ( $detaint_mid ) = $mid =~ /^(\d+)$/;
$mid = $detaint_mid;
print( "Tracker daemon $mid (experimental) starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
print( "Tracker daemon $mid (experimental) starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
my $dbh = zmDbConnect();
my $sql = "select C.*,M.* from Monitors as M left 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 $sql = "SELECT C.*,M.* FROM Monitors as M
LEFT 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( $mid ) or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $res = $sth->execute( $mid )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $monitor = $sth->fetchrow_hashref();
if ( !$monitor )
{
print( "Can't find monitor '$mid'\n" );
exit( -1 );
print( "Can't find monitor '$mid'\n" );
exit( -1 );
}
if ( !$monitor->{Controllable} )
{
print( "Monitor '$mid' is not controllable\n" );
exit( -1 );
print( "Monitor '$mid' is not controllable\n" );
exit( -1 );
}
if ( !$monitor->{TrackMotion} )
{
print( "Monitor '$mid' is not configured to track motion\n" );
exit( -1 );
print( "Monitor '$mid' is not configured to track motion\n" );
exit( -1 );
}
if ( !$monitor->{CanMoveMap} )
{
print( "Monitor '$mid' cannot move in map mode" );
if ( $monitor->{CanMoveRel} )
{
print( ", falling back to pseudo map mode\n" );
}
else
{
print( "\n" );
exit( -1 );
}
print( "Monitor '$mid' cannot move in map mode" );
if ( $monitor->{CanMoveRel} )
{
print( ", falling back to pseudo map mode\n" );
}
else
{
print( "\n" );
exit( -1 );
}
}
Debug( "Found monitor for id '$monitor'\n" );
@ -123,84 +136,100 @@ exit( -1 ) if ( !zmMemVerify( $monitor ) );
sub Suspend
{
my $monitor = shift;
zmMonitorSuspend( $monitor );
my $monitor = shift;
zmMonitorSuspend( $monitor );
}
sub Resume
{
my $monitor = shift;
sleep( $monitor->{TrackDelay} );
zmMonitorResume( $monitor );
my $monitor = shift;
sleep( $monitor->{TrackDelay} );
zmMonitorResume( $monitor );
}
sub Track
{
my $monitor = shift;
my ( $x, $y ) = @_;
my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x;
my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y;
my $monitor = shift;
my ( $x, $y ) = @_;
my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x;
my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y;
my $ctrlCommand = $Config{ZM_PATH_BIN}."/zmcontrol.pl -i ".$monitor->{Id};
$ctrlCommand .= " --command=".($monitor->{CanMoveMap}?"moveMap":"movePseudoMap")." --xcoord=$x --ycoord=$y";
executeShellCommand( $ctrlCommand );
my $ctrlCommand = $Config{ZM_PATH_BIN}
."/zmcontrol.pl -i "
.$monitor->{Id}
;
$ctrlCommand .= " --command="
.( $monitor->{CanMoveMap} ? "moveMap"
: "movePseudoMap"
)
." --xcoord=$x --ycoord=$y"
;
executeShellCommand( $ctrlCommand );
}
sub Return
{
my $monitor = shift;
my $monitor = shift;
my $ctrlCommand = $Config{ZM_PATH_BIN}."/zmcontrol.pl -i ".$monitor->{Id};
if ( $monitor->{ReturnLocation} > 0 )
{
$ctrlCommand .= " --command=presetGoto --preset=".$monitor->{ReturnLocation};
}
else
{
$ctrlCommand .= " --command=presetHome";
}
executeShellCommand( $ctrlCommand );
my $ctrlCommand = $Config{ZM_PATH_BIN}
."/zmcontrol.pl -i "
.$monitor->{Id}
;
if ( $monitor->{ReturnLocation} > 0 )
{
$ctrlCommand .= " --command=presetGoto --preset="
.$monitor->{ReturnLocation}
;
}
else
{
$ctrlCommand .= " --command=presetHome";
}
executeShellCommand( $ctrlCommand );
}
my $last_alarm = 0;
if ( ($monitor->{ReturnLocation} >= 0) )
{
Suspend( $monitor );
Return( $monitor );
Resume( $monitor );
Suspend( $monitor );
Return( $monitor );
Resume( $monitor );
}
my $alarmed = undef;
while( 1 )
{
if ( zmIsAlarmed( $monitor ) )
{
my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation( $monitor );
if ( $alarm_x >= 0 && $alarm_y >= 0 )
{
Debug( "Got alarm at $alarm_x, $alarm_y\n" );
Suspend( $monitor );
Track( $monitor, $alarm_x, $alarm_y );
Resume( $monitor );
$last_alarm = time();
$alarmed = !undef;
}
}
else
{
if ( logDebugging() && $alarmed )
{
print( "Left alarm state\n" );
$alarmed = undef;
}
if ( ($monitor->{ReturnLocation} >= 0) && ($last_alarm > 0) && ((time()-$last_alarm) > $monitor->{ReturnDelay}) )
{
Debug( "Returning to location ".$monitor->{ReturnLocation}."\n" );
Suspend( $monitor );
Return( $monitor );
Resume( $monitor );
$last_alarm = 0;
}
}
usleep( SLEEP_TIME );
if ( zmIsAlarmed( $monitor ) )
{
my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation( $monitor );
if ( $alarm_x >= 0 && $alarm_y >= 0 )
{
Debug( "Got alarm at $alarm_x, $alarm_y\n" );
Suspend( $monitor );
Track( $monitor, $alarm_x, $alarm_y );
Resume( $monitor );
$last_alarm = time();
$alarmed = !undef;
}
}
else
{
if ( logDebugging() && $alarmed )
{
print( "Left alarm state\n" );
$alarmed = undef;
}
if ( ($monitor->{ReturnLocation} >= 0)
&& ($last_alarm > 0)
&& ((time()-$last_alarm) > $monitor->{ReturnDelay})
)
{
Debug( "Returning to location ".$monitor->{ReturnLocation}."\n" );
Suspend( $monitor );
Return( $monitor );
Resume( $monitor );
$last_alarm = 0;
}
}
usleep( SLEEP_TIME );
}

View File

@ -20,11 +20,17 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script is used to trigger and cancel alarms from external connections
# using an arbitrary text based format
#
# ==========================================================================
=head1 NAME
zmtrigger.pl - ZoneMinder External Trigger Script
=head1 DESCRIPTION
This script is used to trigger and cancel alarms from external connections
using an arbitrary text based format
=cut
use strict;
use bytes;
@ -52,8 +58,22 @@ use ZoneMinder::Trigger::Channel::Serial;
use ZoneMinder::Trigger::Connection;
my @connections;
push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan1", channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ), mode=>"rw" ) );
push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan2", channel=>ZoneMinder::Trigger::Channel::Unix->new( path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock' ), mode=>"rw" ) );
push( @connections,
ZoneMinder::Trigger::Connection->new(
name=>"Chan1",
channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ),
mode=>"rw"
)
);
push( @connections,
ZoneMinder::Trigger::Connection->new(
name=>"Chan2",
channel=>ZoneMinder::Trigger::Channel::Unix->new(
path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock'
),
mode=>"rw"
)
);
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan3", channel=>ZoneMinder::Trigger::Channel::File->new( path=>'/tmp/zmtrigger.out' ), mode=>"w" ) );
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan4", channel=>ZoneMinder::Trigger::Channel::Serial->new( path=>'/dev/ttyS0' ), mode=>"rw" ) );
@ -65,7 +85,7 @@ push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan2", channel
use DBI;
#use Socket;
use Data::Dumper;
use autouse 'Data::Dumper'=>qw(Dumper);
use POSIX qw( EINTR );
use Time::HiRes qw( usleep );
@ -85,8 +105,8 @@ my $dbh = zmDbConnect();
my $base_rin = '';
foreach my $connection ( @connections )
{
Info( "Opening connection '$connection->{name}'\n" );
$connection->open();
Info( "Opening connection '$connection->{name}'\n" );
$connection->open();
}
my @in_select_connections = grep { $_->input() && $_->selectable() } @connections;
@ -95,7 +115,7 @@ my @out_connections = grep { $_->output() } @connections;
foreach my $connection ( @in_select_connections )
{
vec( $base_rin, $connection->fileno(), 1 ) = 1;
vec( $base_rin, $connection->fileno(), 1 ) = 1;
}
my %spawned_connections;
@ -111,332 +131,387 @@ my $timeout = SELECT_TIMEOUT;
my %actions;
while( 1 )
{
$rin = $base_rin;
# Add the file descriptors of any spawned connections
foreach my $fileno ( keys(%spawned_connections) )
{
vec( $rin, $fileno, 1 ) = 1;
}
$rin = $base_rin;
# Add the file descriptors of any spawned connections
foreach my $fileno ( keys(%spawned_connections) )
{
vec( $rin, $fileno, 1 ) = 1;
}
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
if ( $nfound > 0 )
{
Debug( "Got input from $nfound connections\n" );
foreach my $connection ( @in_select_connections )
{
if ( vec( $rout, $connection->fileno(), 1 ) )
{
Debug( "Got input from connection ".$connection->name()." (".$connection->fileno().")\n" );
if ( $connection->spawns() )
{
my $new_connection = $connection->accept();
$spawned_connections{$new_connection->fileno()} = $new_connection;
Debug( "Added new spawned connection (".$new_connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" );
}
else
{
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
}
}
foreach my $connection ( values(%spawned_connections) )
{
if ( vec( $rout, $connection->fileno(), 1 ) )
{
Debug( "Got input from spawned connection ".$connection->name()." (".$connection->fileno().")\n" );
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
else
{
delete( $spawned_connections{$connection->fileno()} );
Debug( "Removed spawned connection (".$connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" );
$connection->close();
}
}
}
}
elsif ( $nfound < 0 )
{
if ( $! == EINTR )
{
# Do nothing
}
else
{
Fatal( "Can't select: $!" );
}
}
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
if ( $nfound > 0 )
{
Debug( "Got input from $nfound connections\n" );
foreach my $connection ( @in_select_connections )
{
if ( vec( $rout, $connection->fileno(), 1 ) )
{
Debug( "Got input from connection "
.$connection->name()
." ("
.$connection->fileno()
.")\n"
);
if ( $connection->spawns() )
{
my $new_connection = $connection->accept();
$spawned_connections{$new_connection->fileno()} = $new_connection;
Debug( "Added new spawned connection ("
.$new_connection->fileno()
."), "
.int(keys(%spawned_connections))
." spawned connections\n"
);
}
else
{
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
}
}
foreach my $connection ( values(%spawned_connections) )
{
if ( vec( $rout, $connection->fileno(), 1 ) )
{
Debug( "Got input from spawned connection "
.$connection->name()
." ("
.$connection->fileno()
.")\n"
);
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
else
{
delete( $spawned_connections{$connection->fileno()} );
Debug( "Removed spawned connection ("
.$connection->fileno()
."), "
.int(keys(%spawned_connections))
." spawned connections\n"
);
$connection->close();
}
}
}
}
elsif ( $nfound < 0 )
{
if ( $! == EINTR )
{
# Do nothing
}
else
{
Fatal( "Can't select: $!" );
}
}
# Check polled connections
foreach my $connection ( @in_poll_connections )
{
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
# Check polled connections
foreach my $connection ( @in_poll_connections )
{
my $messages = $connection->getMessages();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
# Check for alarms that might have happened
my @out_messages;
foreach my $monitor ( values(%monitors) )
{
my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] );
# Check for alarms that might have happened
my @out_messages;
foreach my $monitor ( values(%monitors) )
{
my ( $state, $last_event )
= zmMemRead( $monitor,
[ "shared_data:state",
"shared_data:last_event"
]
);
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
if ( $state == STATE_ALARM || $state == STATE_ALERT ) # In alarm state
{
if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent}) ) # A new event
{
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
}
else # The same one as last time, so ignore it
{
# Do nothing
}
}
elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) ) # Out of alarm state
{
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
}
elsif ( defined($monitor->{LastEvent}) && ($last_event != $monitor->{LastEvent}) ) # We've missed a whole event
{
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
}
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
}
foreach my $connection ( @out_connections )
{
if ( $connection->canWrite() )
{
$connection->putMessages( \@out_messages );
}
}
foreach my $connection ( values(%spawned_connections) )
{
if ( $connection->canWrite() )
{
$connection->putMessages( \@out_messages );
}
}
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
if ( $state == STATE_ALARM
|| $state == STATE_ALERT
) # In alarm state
{
if ( !defined($monitor->{LastEvent})
|| ($last_event != $monitor->{LastEvent})
) # A new event
{
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
}
else # The same one as last time, so ignore it
{
# Do nothing
}
}
elsif ( ($state == STATE_IDLE
&& $monitor->{LastState} != STATE_IDLE
)
|| ($state == STATE_TAPE
&& $monitor->{LastState} != STATE_TAPE
)
) # Out of alarm state
{
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
}
elsif ( defined($monitor->{LastEvent})
&& ($last_event != $monitor->{LastEvent})
) # We've missed a whole event
{
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
}
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
}
foreach my $connection ( @out_connections )
{
if ( $connection->canWrite() )
{
$connection->putMessages( \@out_messages );
}
}
foreach my $connection ( values(%spawned_connections) )
{
if ( $connection->canWrite() )
{
$connection->putMessages( \@out_messages );
}
}
Debug( "Checking for timed actions\n" ) if ( int(keys(%actions)) );
my $now = time();
foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) )
{
Info( "Found actions expiring at $action_time\n" );
foreach my $action ( @{$actions{$action_time}} )
{
my $connection = $action->{connection};
my $message = $action->{message};
Info( "Found action '$message'\n" );
handleMessage( $connection, $message );
}
delete( $actions{$action_time} );
}
Debug( "Checking for timed actions\n" )
if ( int(keys(%actions)) );
my $now = time();
foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) )
{
Info( "Found actions expiring at $action_time\n" );
foreach my $action ( @{$actions{$action_time}} )
{
my $connection = $action->{connection};
my $message = $action->{message};
Info( "Found action '$message'\n" );
handleMessage( $connection, $message );
}
delete( $actions{$action_time} );
}
# Allow connections to do their own timed actions
foreach my $connection ( @connections )
{
my $messages = $connection->timedActions();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
foreach my $connection ( values(%spawned_connections) )
{
my $messages = $connection->timedActions();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
foreach my $connection ( @connections )
{
my $messages = $connection->timedActions();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
foreach my $connection ( values(%spawned_connections) )
{
my $messages = $connection->timedActions();
if ( defined($messages) )
{
foreach my $message ( @$messages )
{
handleMessage( $connection, $message );
}
}
}
# If necessary reload monitors
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )
{
foreach my $monitor ( values(%monitors) )
# If necessary reload monitors
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )
{
foreach my $monitor ( values(%monitors) )
{
# Free up any used memory handle
zmMemInvalidate( $monitor );
}
loadMonitors();
}
loadMonitors();
}
}
Info( "Trigger daemon exiting\n" );
exit;
sub loadMonitors
{
Debug( "Loading monitors\n" );
$monitor_reload_time = time();
Debug( "Loading monitors\n" );
$monitor_reload_time = time();
my %new_monitors = ();
my %new_monitors = ();
my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' )";
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() )
{
next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
my $sql = "SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )"
;
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() )
{
next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
if ( defined($monitors{$monitor->{Id}}->{LastState}) )
{
$monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState};
}
else
{
$monitor->{LastState} = zmGetMonitorState( $monitor );
}
if ( defined($monitors{$monitor->{Id}}->{LastEvent}) )
{
$monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent};
}
else
{
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
$new_monitors{$monitor->{Id}} = $monitor;
}
%monitors = %new_monitors;
if ( defined($monitors{$monitor->{Id}}->{LastState}) )
{
$monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState};
}
else
{
$monitor->{LastState} = zmGetMonitorState( $monitor );
}
if ( defined($monitors{$monitor->{Id}}->{LastEvent}) )
{
$monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent};
}
else
{
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
$new_monitors{$monitor->{Id}} = $monitor;
}
%monitors = %new_monitors;
}
sub handleMessage
{
my $connection = shift;
my $message = shift;
my $connection = shift;
my $message = shift;
my ( $id, $action, $score, $cause, $text, $showtext ) = split( /\|/, $message );
$score = 0 if ( !defined($score) );
$cause = "" if ( !defined($cause) );
$text = "" if ( !defined($text) );
my ( $id, $action, $score, $cause, $text, $showtext )
= split( /\|/, $message );
$score = 0 if ( !defined($score) );
$cause = "" if ( !defined($cause) );
$text = "" if ( !defined($text) );
my $monitor = $monitors{$id};
if ( !$monitor )
{
Warning( "Can't find monitor '$id' for message '$message'\n" );
return;
}
Debug( "Found monitor for id '$id'\n" );
my $monitor = $monitors{$id};
if ( !$monitor )
{
Warning( "Can't find monitor '$id' for message '$message'\n" );
return;
}
Debug( "Found monitor for id '$id'\n" );
next if ( !zmMemVerify( $monitor ) );
next if ( !zmMemVerify( $monitor ) );
Debug( "Handling action '$action'\n" );
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ )
{
my $state = $1;
my $delay = $2;
if ( $state eq "enable" )
{
zmMonitorEnable( $monitor );
}
else
{
zmMonitorDisable( $monitor );
}
# Force a reload
$monitor_reload_time = 0;
Info( "Set monitor to $state\n" );
if ( $delay )
{
my $action_time = time()+$delay;
my $action_text = $id."|".(($state eq "enable")?"disable":"enable");
my $action_array = $actions{$action_time};
if ( !$action_array )
{
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection, message=>$action_text } );
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
}
}
elsif ( $action =~ /^(on|off)(?:\+(\d+))?$/ )
{
next if ( !$monitor->{Enabled} );
Debug( "Handling action '$action'\n" );
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ )
{
my $state = $1;
my $delay = $2;
if ( $state eq "enable" )
{
zmMonitorEnable( $monitor );
}
else
{
zmMonitorDisable( $monitor );
}
# Force a reload
$monitor_reload_time = 0;
Info( "Set monitor to $state\n" );
if ( $delay )
{
my $action_time = time()+$delay;
my $action_text = $id."|".( ($state eq "enable")
? "disable"
: "enable"
)
;
my $action_array = $actions{$action_time};
if ( !$action_array )
{
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection,
message=>$action_text
}
);
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
}
}
elsif ( $action =~ /^(on|off)(?:\+(\d+))?$/ )
{
next if ( !$monitor->{Enabled} );
my $trigger = $1;
my $delay = $2;
my $trigger_data;
if ( $trigger eq "on" )
{
zmTriggerEventOn( $monitor, $score, $cause, $text );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger' '$cause'\n" );
}
elsif ( $trigger eq "off" )
{
my $trigger = $1;
my $delay = $2;
my $trigger_data;
if ( $trigger eq "on" )
{
zmTriggerEventOn( $monitor, $score, $cause, $text );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger' '$cause'\n" );
}
elsif ( $trigger eq "off" )
{
my $last_event = zmGetLastEvent( $monitor );
zmTriggerEventOff( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger'\n" );
zmTriggerEventOff( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger'\n" );
# Wait til it's finished
while( zmInAlarm( $monitor ) && ($last_event == zmGetLastEvent( $monitor )) )
while( zmInAlarm( $monitor )
&& ($last_event == zmGetLastEvent( $monitor ))
)
{
# Tenth of a second
usleep( 100000 );
}
zmTriggerEventCancel( $monitor );
}
else
{
Info( "Trigger '$trigger'\n" );
zmTriggerEventCancel( $monitor );
}
if ( $delay )
{
my $action_time = time()+$delay;
#my $action_text = $id."|cancel|0|".$cause."|".$text;
my $action_text = $id."|cancel";
my $action_array = $actions{$action_time};
if ( !$action_array )
{
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection, message=>$action_text } );
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
}
}
elsif( $action eq "cancel" )
{
zmTriggerEventCancel( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Cancelled event\n" );
}
elsif( $action eq "show" )
{
zmTriggerShowtext( $monitor, $showtext );
Info( "Updated show text to '$showtext'\n" );
}
else
{
Error( "Unrecognised action '$action' in message '$message'\n" );
}
zmTriggerEventCancel( $monitor );
}
else
{
Info( "Trigger '$trigger'\n" );
zmTriggerEventCancel( $monitor );
}
if ( $delay )
{
my $action_time = time()+$delay;
#my $action_text = $id."|cancel|0|".$cause."|".$text;
my $action_text = $id."|cancel";
my $action_array = $actions{$action_time};
if ( !$action_array )
{
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection,
message=>$action_text
}
);
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
}
}
elsif( $action eq "cancel" )
{
zmTriggerEventCancel( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Cancelled event\n" );
}
elsif( $action eq "show" )
{
zmTriggerShowtext( $monitor, $showtext );
Info( "Updated show text to '$showtext'\n" );
}
else
{
Error( "Unrecognised action '$action' in message '$message'\n" );
}
} # end sub handleMessage
1;

View File

@ -20,11 +20,34 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script just checks what the most recent release of ZoneMinder is
# at the the moment. It will eventually be responsible for applying and
# configuring upgrades etc, including on the fly upgrades.
#
=head1 NAME
zmupdate.pl - check and upgrade Zoneminer database
=head1 SYNOPSIS
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u<dbuser> -p<dbpass>]
=head1 DESCRIPTION
This script just checks what the most recent release of ZoneMinder is
at the the moment. It will eventually be responsible for applying and
configuring upgrades etc, including on the fly upgrades.
=head1 OPTIONS
-c, --check - Check for updated versions of ZoneMinder
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
-v<version>, --version=<version> - Force upgrade to the current version from <version>
-u<dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
-p<dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
-d<dir>,--dir=<dir> - Directory containing update files if not in default build location
-interactive - interact with the user
-nointeractive - do not interact with the user
=cut
use strict;
use bytes;
@ -52,7 +75,8 @@ use ZoneMinder::ConfigAdmin qw( :functions );
use POSIX;
use DBI;
use Getopt::Long;
use Data::Dumper;
use autouse 'Pod::Usage'=>qw(pod2usage);
use autouse 'Data::Dumper'=>qw(Dumper);
use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS});
@ -78,27 +102,19 @@ my $version = '';
my $dbUser = $Config{ZM_DB_USER};
my $dbPass = $Config{ZM_DB_PASS};
my $updateDir = '';
sub Usage
{
print( "
Usage: zmupdate.pl <-c,--check|-f,--freshen|-v<version>,--version=<version>> [-u<dbuser> -p<dbpass>]>
Parameters are :-
-c, --check - Check for updated versions of ZoneMinder
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
-v<version>, --version=<version> - Force upgrade to the current version from <version>
-u<dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
-p<dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
-d<dir>,--dir=<dir> - Directory containing update files if not in default build location
-interactive - interact with the user
-nointeractive - do not interact with the user
");
exit( -1 );
}
if ( !GetOptions( 'check'=>\$check, 'freshen'=>\$freshen, 'rename'=>\$rename, 'zone-fix'=>\$zoneFix, 'migrate-events'=>\$migrateEvents, 'version=s'=>\$version, 'interactive!'=>\$interactive, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass, 'dir:s'=>\$updateDir ) )
{
Usage();
}
GetOptions(
'check' =>\$check,
'freshen' =>\$freshen,
'rename' =>\$rename,
'zone-fix' =>\$zoneFix,
'migrate-events' =>\$migrateEvents,
'version=s' =>\$version,
'interactive!' =>\$interactive,
'user:s' =>\$dbUser,
'pass:s' =>\$dbPass,
'dir:s' =>\$updateDir
) or pod2usage(-exitstatus => -1);
my $dbh = zmDbConnect();
$Config{ZM_DB_USER} = $dbUser;
@ -113,14 +129,14 @@ if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version)
else
{
print( STDERR "Please give a valid option\n" );
Usage();
pod2usage(-exitstatus => -1);
}
}
if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0)) > 1 )
{
print( STDERR "Please give only one option\n" );
Usage();
pod2usage(-exitstatus => -1);
}
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} )

View File

@ -20,10 +20,37 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This script is used to create MPEG videos of events for the web pages
# or as email attachments.
#
=head1 NAME
zmvideo.pl - ZoneMinder Video Creation Script
=head1 SYNOPSIS
zmvideo.pl -e <event_id>,--event=<event_id> [--format <format>]
[--rate=<rate>]
[--scale=<scale>]
[--fps=<fps>]
[--size=<size>]
[--overwrite]
=head1 DESCRIPTION
This script is used to create MPEG videos of events for the web pages
or as email attachments.
=head1 OPTIONS
-e<event_id>, --event=<event_id> - What event to create the video for
-f<format>, --format=<format> - What format to create the video in, default is mpg. For ffmpeg only.
-r<rate>, --rate=<rate> - Relative rate, 1 = realtime, 2 = double speed, 0.5 = half speed etc.
-s<scale>, --scale=<scale> - Scale, 1 = normal, 2 = double size, 0.5 = half size etc.
-F<fps>, --fps=<fps> - Absolute frame rate, in frames per second
-S<size>, --size=<size> - Absolute video size, WxH or other specification supported by ffmpeg
-o, --overwrite - Whether to overwrite an existing file, off by default.
-v, --version - Outputs the currently installed version of ZoneMinder
=cut
use strict;
use bytes;
@ -36,9 +63,10 @@ use bytes;
@EXTRA_PERL_LIB@
use ZoneMinder;
use DBI;
use Data::Dumper;
use autouse 'Data::Dumper'=>qw(Dumper);
use POSIX qw(strftime);
use Getopt::Long qw(:config no_ignore_case );
use autouse 'Pod::Usage'=>qw(pod2usage);
$| = 1;
@ -60,77 +88,66 @@ my $version = 0;
my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} );
for ( my $i = 0; $i < @formats; $i++ )
{
if ( $i =~ /^(.+)\*$/ )
{
$format = $formats[$i] = $1;
}
if ( $i =~ /^(.+)\*$/ )
{
$format = $formats[$i] = $1;
}
}
sub Usage
{
print( "
Usage: zmvideo.pl -e <event_id>,--event=<event_id> [--format <format>] [--rate=<rate>] [--scale=<scale>] [--fps=<fps>] [--size=<size>] [--overwrite]
Parameters are :-
-e<event_id>, --event=<event_id> - What event to create the video for
-f<format>, --format=<format> - What format to create the video in, default is mpg. For ffmpeg only.
-r<rate>, --rate=<rate> - Relative rate , 1 = realtime, 2 = double speed , 0.5 = half speed etc
-s<scale>, --scale=<scale> - Scale, 1 = normal, 2 = double size, 0.5 = half size etc
-F<fps>, --fps=<fps> - Absolute frame rate, in frames per second
-S<size>, --size=<size> - Absolute video size, WxH or other specification supported by ffmpeg
-o, --overwrite - Whether to overwrite an existing file, off by default.
-v, --version - Outputs the currently installed version of ZoneMinder
");
exit( -1 );
}
if ( !GetOptions( 'event=i'=>\$event_id, 'format|f=s'=>\$format, 'rate|r=f'=>\$rate, 'scale|s=f'=>\$scale, 'fps|F=f'=>\$fps, 'size|S=s'=>\$size, 'overwrite'=>\$overwrite, version=>\$version ) )
{
Usage();
}
GetOptions(
'event=i' =>\$event_id,
'format|f=s' =>\$format,
'rate|r=f' =>\$rate,
'scale|s=f' =>\$scale,
'fps|F=f' =>\$fps,
'size|S=s' =>\$size,
'overwrite' =>\$overwrite,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
if ( $version ) {
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
print ZoneMinder::Base::ZM_VERSION . "\n";
exit(0);
}
if ( !$event_id || $event_id < 0 )
{
print( STDERR "Please give a valid event id\n" );
Usage();
print( STDERR "Please give a valid event id\n" );
pod2usage(-exitstatus => -1);
}
if ( ! $Config{ZM_OPT_FFMPEG} )
{
print( STDERR "Mpeg encoding is not currently enabled\n" );
exit(-1);
print( STDERR "Mpeg encoding is not currently enabled\n" );
exit(-1);
}
if ( !$rate && !$fps )
{
$rate = 1;
$rate = 1;
}
if ( !$scale && !$size )
{
$scale = 1;
$scale = 1;
}
if ( $rate && ($rate < 0.25 || $rate > 100) )
{
print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" );
Usage();
print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" );
pod2usage(-exitstatus => -1);
}
if ( $scale && ($scale < 0.25 || $scale > 4) )
{
print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" );
Usage();
print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" );
pod2usage(-exitstatus => -1);
}
if ( $fps && ($fps > 30) )
{
print( STDERR "FPS is out of range, <= 30\n" );
Usage();
print( STDERR "FPS is out of range, <= 30\n" );
pod2usage(-exitstatus => -1);
}
my ( $detaint_format ) = $format =~ /^(\w+)$/;
@ -148,9 +165,23 @@ $size = $detaint_size;
my $dbh = zmDbConnect();
my @filters;
my $sql = "select max(F.Delta)-min(F.Delta) as FullLength, E.*, unix_timestamp(E.StartTime) as Time, M.Name as MonitorName, M.Width as MonitorWidth, M.Height as MonitorHeight, M.Palette from Frames as F inner join Events as E on F.EventId = E.Id inner join Monitors as M on E.MonitorId = M.Id where EventId = '$event_id' group by F.EventId";
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() );
my $sql = " SELECT max(F.Delta)-min(F.Delta) as FullLength,
E.*,
unix_timestamp(E.StartTime) as Time,
M.Name as MonitorName,
M.Width as MonitorWidth,
M.Height as MonitorHeight,
M.Palette
FROM Frames as F
INNER JOIN Events as E on F.EventId = E.Id
INNER JOIN Monitors as M on E.MonitorId = M.Id
WHERE EventId = '$event_id'
GROUP BY F.EventId"
;
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() );
my $event = $sth->fetchrow_hashref();
$sth->finish();
my $event_path = getEventPath( $event );
@ -160,42 +191,42 @@ chdir( $event_path );
my @file_parts;
if ( $rate )
{
my $file_rate = $rate;
$file_rate =~ s/\./_/;
$file_rate =~ s/_00//;
$file_rate =~ s/(_\d+)0+$/$1/;
$file_rate = 'r'.$file_rate;
push( @file_parts, $file_rate );
my $file_rate = $rate;
$file_rate =~ s/\./_/;
$file_rate =~ s/_00//;
$file_rate =~ s/(_\d+)0+$/$1/;
$file_rate = 'r'.$file_rate;
push( @file_parts, $file_rate );
}
elsif ( $fps )
{
my $file_fps = $fps;
$file_fps =~ s/\./_/;
$file_fps =~ s/_00//;
$file_fps =~ s/(_\d+)0+$/$1/;
$file_fps = 'R'.$file_fps;
push( @file_parts, $file_fps );
my $file_fps = $fps;
$file_fps =~ s/\./_/;
$file_fps =~ s/_00//;
$file_fps =~ s/(_\d+)0+$/$1/;
$file_fps = 'R'.$file_fps;
push( @file_parts, $file_fps );
}
if ( $scale )
{
my $file_scale = $scale;
$file_scale =~ s/\./_/;
$file_scale =~ s/_00//;
$file_scale =~ s/(_\d+)0+$/$1/;
$file_scale = 's'.$file_scale;
push( @file_parts, $file_scale );
my $file_scale = $scale;
$file_scale =~ s/\./_/;
$file_scale =~ s/_00//;
$file_scale =~ s/(_\d+)0+$/$1/;
$file_scale = 's'.$file_scale;
push( @file_parts, $file_scale );
}
elsif ( $size )
{
my $file_size = 'S'.$size;
push( @file_parts, $file_size );
my $file_size = 'S'.$size;
push( @file_parts, $file_size );
}
my $video_file = "$video_name-".$file_parts[0]."-".$file_parts[1].".$format";
if ( $overwrite || !-s $video_file )
{
Info( "Creating video file $video_file for event $event->{Id}\n" );
Info( "Creating video file $video_file for event $event->{Id}\n" );
my $frame_rate = sprintf( "%.2f", $event->{Frames}/$event->{FullLength} );
if ( $rate )
@ -228,22 +259,32 @@ if ( $overwrite || !-s $video_file )
$video_size = $size;
}
my $command = $Config{ZM_PATH_FFMPEG}." -y -r $frame_rate ".$Config{ZM_FFMPEG_INPUT_OPTIONS}." -i %0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg -s $video_size ".$Config{ZM_FFMPEG_OUTPUT_OPTIONS}." '$video_file' > ffmpeg.log 2>&1";
my $command = $Config{ZM_PATH_FFMPEG}
." -y -r $frame_rate "
.$Config{ZM_FFMPEG_INPUT_OPTIONS}
." -i %0"
.$Config{ZM_EVENT_IMAGE_DIGITS}
."d-capture.jpg -s $video_size "
.$Config{ZM_FFMPEG_OUTPUT_OPTIONS}
." '$video_file' > ffmpeg.log 2>&1"
;
Debug( $command."\n" );
my $output = qx($command);
my $status = $? >> 8;
if ( $status )
{
Error( "Unable to generate video, check ".$event_path."/ffmpeg.log for details" );
Error( "Unable to generate video, check "
.$event_path."/ffmpeg.log for details"
);
exit( -1 );
}
Info( "Finished $video_file\n" );
Info( "Finished $video_file\n" );
}
else
{
Info( "Video file $video_file already exists for event $event->{Id}\n" );
Info( "Video file $video_file already exists for event $event->{Id}\n" );
}
#print( STDOUT $event->{MonitorId}.'/'.$event->{Id}.'/'.$video_file."\n" );
print( STDOUT $event_path . '/' . $video_file ."\n" );

View File

@ -20,11 +20,22 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This does some basic setup for ZoneMinder to run and then periodically
# checks the fps output of the active daemons to check they haven't
# locked up. If they have then they are killed and restarted
#
=head1 NAME
zmwatch.pl - ZoneMinder WatchDog Script
=head1 SYNOPSIS
zmwatch.pl
=head1 DESCRIPTION
This does some basic setup for ZoneMinder to run and then periodically
checks the fps output of the active daemons to check they haven't
locked up. If they have then they are killed and restarted
=cut
use strict;
use bytes;
@ -46,7 +57,7 @@ use constant START_DELAY => 30; # To give everything else time to start
use ZoneMinder;
use POSIX;
use DBI;
use Data::Dumper;
use autouse 'Data::Dumper'=>qw(Dumper);
$| = 1;
@ -54,14 +65,6 @@ $ENV{PATH} = '/bin:/usr/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
sub Usage
{
print( "
Usage: zmwatch.pl
");
exit( -1 );
}
logInit();
logSetSignal();
@ -72,38 +75,49 @@ sleep( START_DELAY );
my $dbh = zmDbConnect();
my $sql = "select * from Monitors";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
while( 1 )
{
my $now = time();
my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
if ( $monitor->{Function} ne 'None' )
{
my $now = time();
my $res = $sth->execute()
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
if ( $monitor->{Function} ne 'None' )
{
my $restart = 0;
if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) )
{
# Check we have got an image recently
my $image_time = zmGetLastWriteTime( $monitor );
next if ( !defined($image_time) ); # Can't read from shared data
next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
if ( zmMemVerify( $monitor )
&& zmMemRead( $monitor, "shared_data:valid" )
)
{
# Check we have got an image recently
my $image_time = zmGetLastWriteTime( $monitor );
next if ( !defined($image_time) ); # Can't read from shared data
next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):$Config{ZM_WATCH_MAX_DELAY};
my $image_delay = $now-$image_time;
Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" );
if ( $image_delay > $max_image_delay )
{
Info( "Restarting capture daemon for ".$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n" );
my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now-$image_time;
Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" );
if ( $image_delay > $max_image_delay )
{
Info( "Restarting capture daemon for "
.$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n"
);
$restart = 1;
}
}
else
{
#Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" );
}
else
{
#Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" );
#$restart = 1;
}
}
if ( $restart )
{
@ -120,19 +134,28 @@ while( 1 )
}
elsif ( $monitor->{Function} ne 'Monitor' )
{
if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) )
if ( zmMemVerify( $monitor )
&& zmMemRead( $monitor, "shared_data:valid" )
)
{
# Check we have got an image recently
my $image_time = zmGetLastReadTime( $monitor );
next if ( !defined($image_time) ); # Can't read from shared data
next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):$Config{ZM_WATCH_MAX_DELAY};
my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now-$image_time;
Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
if ( $image_delay > $max_image_delay )
{
Info( "Restarting analysis daemon for ".$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n" );
Info( "Restarting analysis daemon for "
.$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n"
);
my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
runCommand( $command );
}
@ -141,8 +164,8 @@ while( 1 )
}
# Prevent open handles building up if we have connect to shared memory
zmMemInvalidate( $monitor );
}
sleep( $Config{ZM_WATCH_CHECK_INTERVAL} );
}
sleep( $Config{ZM_WATCH_CHECK_INTERVAL} );
}
Info( "Watchdog exiting\n" );
exit();

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@ add_executable(zma zma.cpp)
add_executable(zmu zmu.cpp)
add_executable(zmf zmf.cpp)
add_executable(zms zms.cpp)
add_executable(nph-zms zms.cpp)
add_executable(zmstreamer zmstreamer.cpp)
target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
@ -22,10 +21,9 @@ target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmf zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(nph-zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmstreamer zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
install(TARGETS zmc zma zmu zmf zmstreamer RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(TARGETS zms nph-zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(TARGETS zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(CODE "execute_process(COMMAND ln -sf zms nph-zms WORKING_DIRECTORY \"\$ENV{DESTDIR}${ZM_CGIDIR}\")")

View File

@ -17,14 +17,24 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#if !defined(PATH_MAX)
#define PATH_MAX 1024
#endif
#ifndef ZM_H
#define ZM_H
#include "zm_config.h"
#ifdef SOLARIS
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
#include <string.h> // define strerror() and friends
#endif
#include "zm_logger.h"
#include <stdint.h>
#include <iostream>
extern const char* self;
#endif // ZM_H

View File

@ -23,7 +23,11 @@
#include "zm.h"
#include "zm_coord.h"
#ifndef SOLARIS
#include <math.h>
#else
#include <cmath>
#endif
//
// Class used for storing a box, which is defined as a region

View File

@ -36,6 +36,10 @@
#include <sys/param.h>
#include <netinet/tcp.h>
#ifdef SOLARIS
#include <sys/filio.h> // define FIONREAD
#endif
int CommsBase::readV( int iovcnt, /* const void *, int, */ ... )
{
va_list arg_ptr;

View File

@ -36,6 +36,12 @@
#include "zm_event.h"
#include "zm_monitor.h"
// sendfile tricks
extern "C"
{
#include "zm_sendfile.h"
}
#include "zmf.h"
#if HAVE_SYS_SENDFILE_H
@ -1431,7 +1437,7 @@ bool EventStream::sendFrame( int delta_us )
if(send_raw) {
#if HAVE_SENDFILE
fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size );
if(sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) {
if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) {
/* sendfile() failed, use standard way instead */
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
@ -1544,8 +1550,10 @@ void EventStream::runStream()
if ( ((curr_frame_id-1)%frame_mod) == 0 )
{
delta_us = (unsigned int)(frame_data->delta * 1000000);
if ( effective_fps < base_fps )
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
// if effective > base we should speed up frame delivery
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;
}
}

View File

@ -86,13 +86,21 @@ SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL),
Debug(4,"SWScale object created");
/* Allocate AVFrame for the input */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
input_avframe = av_frame_alloc();
#else
input_avframe = avcodec_alloc_frame();
#endif
if(input_avframe == NULL) {
Fatal("Failed allocating AVFrame for the input");
}
/* Allocate AVFrame for the output */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
output_avframe = av_frame_alloc();
#else
output_avframe = avcodec_alloc_frame();
#endif
if(output_avframe == NULL) {
Fatal("Failed allocating AVFrame for the output");
}
@ -140,7 +148,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
return -3;
}
#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0, 8, 0)
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
/* Warn if the input or output pixelformat is not supported */
if(!sws_isSupportedInput(in_pf)) {
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));

View File

@ -32,7 +32,17 @@ extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/base64.h>
#include <libavutil/mathematics.h>
#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(50, 28, 0)
/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
#include <libavutil/opt.h>
#else
#include <libavcodec/opt.h>
@ -42,43 +52,82 @@ extern "C" {
#include <ffmpeg/base64.h>
#include <ffmpeg/mathematics.h>
#include <ffmpeg/opt.h>
#endif
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
// AVCODEC
#if HAVE_LIBAVCODEC_AVCODEC_H
#include <libavcodec/avcodec.h>
/*
* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVCODEC_H
#include <ffmpeg/avcodec.h>
#endif
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
#if defined(HAVE_LIBAVCODEC_AVCODEC_H)
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0)
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
#define _AVCODECID AVCodecID
#else
#define _AVCODECID CodecID
#endif
#endif
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
// AVFORMAT
#if HAVE_LIBAVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
/* LIBAVFORMAT_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVFORMAT_H
#include <ffmpeg/avformat.h>
#endif
#endif /* HAVE_LIBAVFORMAT_AVFORMAT_H */
// AVDEVICE
#if HAVE_LIBAVDEVICE_AVDEVICE_H
#include <libavdevice/avdevice.h>
/* LIBAVDEVICE_VERSION_CHECK checks for the right version of libav and FFmpeg
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVDEVICE_H
#include <ffmpeg/avdevice.h>
#endif
#endif /* HAVE_LIBAVDEVICE_AVDEVICE_H */
// SWSCALE
#if HAVE_LIBSWSCALE_SWSCALE_H
#include <libswscale/swscale.h>
/* LIBSWSCALE_VERSION_CHECK checks for the right version of libav and FFmpeg
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_SWSCALE_H
#include <ffmpeg/swscale.h>
#endif
#endif /* HAVE_LIBSWSCALE_SWSCALE_H */
#ifdef __cplusplus
}
@ -86,7 +135,7 @@ extern "C" {
#if ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0)
#if !LIBAVFORMAT_VERSION_CHECK(52, 107, 0, 107, 0)
#if defined(AVIO_WRONLY)
#define AVIO_FLAG_WRITE AVIO_WRONLY
#else
@ -131,7 +180,7 @@ protected:
};
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 25, 0)
#if !LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
#define AV_CODEC_ID_NONE CODEC_ID_NONE
#define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW
#define AV_CODEC_ID_PCM_ALAW CODEC_ID_PCM_ALAW
@ -167,18 +216,18 @@ protected:
inline static const std::string av_make_error_string(int errnum)
{
char errbuf[AV_ERROR_MAX_STRING_SIZE];
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(50, 12, 13)
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
#else
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
#endif
return (std::string)errbuf;
}
#undef av_err2str
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
#endif // __cplusplus
#endif // __cplusplus
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )

View File

@ -30,6 +30,12 @@ extern "C"{
#define AV_ERROR_MAX_STRING_SIZE 64
#endif
#ifdef SOLARIS
#include <sys/errno.h> // for ESRCH
#include <signal.h>
#include <pthread.h>
#endif
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
mPath( p_path ),
@ -128,7 +134,7 @@ int FfmpegCamera::Capture( Image &image )
void *retval = 0;
int ret;
ret = pthread_tryjoin_np(mReopenThread, &retval);
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
@ -172,7 +178,7 @@ int FfmpegCamera::Capture( Image &image )
Debug( 5, "Got packet from stream %d", packet.stream_index );
if ( packet.stream_index == mVideoStreamId )
{
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0)
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
#else
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
@ -226,7 +232,7 @@ int FfmpegCamera::OpenFfmpeg() {
mIsOpening = true;
// Open the input, not necessarily a file
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0)
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
Debug ( 1, "Calling av_open_input_file" );
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 )
#else
@ -283,7 +289,7 @@ int FfmpegCamera::OpenFfmpeg() {
//mFormatContext->probesize = 32;
//mFormatContext->max_analyze_duration = 32;
// Locate stream info from avformat_open_input
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0)
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
Debug ( 1, "Calling av_find_stream_info" );
if ( av_find_stream_info( mFormatContext ) < 0 )
#else
@ -291,15 +297,14 @@ int FfmpegCamera::OpenFfmpeg() {
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 )
#endif
Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) );
Info( "Find stream info complete %s", mPath.c_str() );
Debug ( 1, "Got stream info" );
// Find first video stream present
mVideoStreamId = -1;
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
{
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1)
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
@ -310,7 +315,7 @@ int FfmpegCamera::OpenFfmpeg() {
}
if(mAudioStreamId == -1) //FIXME best way to copy all other streams?
{
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1)
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
@ -334,7 +339,7 @@ int FfmpegCamera::OpenFfmpeg() {
Debug ( 1, "Found decoder" );
// Open the codec
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0)
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug ( 1, "Calling avcodec_open" );
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
#else
@ -346,11 +351,19 @@ int FfmpegCamera::OpenFfmpeg() {
Debug ( 1, "Opened codec" );
// Allocate space for the native video frame
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
mRawFrame = av_frame_alloc();
#else
mRawFrame = avcodec_alloc_frame();
#endif
// Allocate space for the converted video frame
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
mFrame = av_frame_alloc();
#else
mFrame = avcodec_alloc_frame();
#endif
if(mRawFrame == NULL || mFrame == NULL)
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
@ -377,11 +390,9 @@ int FfmpegCamera::OpenFfmpeg() {
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
#endif // HAVE_LIBSWSCALE
Info( "Primed capture from %s, video=%d, audio=%d", mPath.c_str(), mVideoStreamId, mAudioStreamId);
mCanCapture = true;
mCanCapture = true;
return( 0 );
return 0;
}
int FfmpegCamera::ReopenFfmpeg() {
@ -421,7 +432,7 @@ int FfmpegCamera::CloseFfmpeg(){
}
if ( mFormatContext )
{
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0)
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( mFormatContext );
#else
avformat_close_input( &mFormatContext );
@ -455,7 +466,7 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
// Close current stream.
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);
wait = wait < 0 ? 0 : wait;
if (wait > 0){
@ -481,7 +492,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
void *retval = 0;
int ret;
ret = pthread_tryjoin_np(mReopenThread, &retval);
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
@ -525,7 +536,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
Debug( 5, "Got packet from stream %d", packet.stream_index );
if ( packet.stream_index == mVideoStreamId )
{
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0)
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
#else
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )

View File

@ -104,13 +104,13 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin
pixels = width*height;
colours = p_colours;
subpixelorder = p_subpixelorder;
size = (width*height)*colours;
size = pixels*colours;
buffer = 0;
holdbuffer = 0;
if ( p_buffer )
{
allocation = size;
buffertype = ZM_BUFTYPE_DONTFREE;
allocation = size;
buffertype = ZM_BUFTYPE_DONTFREE;
buffer = p_buffer;
}
else
@ -129,10 +129,10 @@ Image::Image( const Image &p_image )
pixels = p_image.pixels;
colours = p_image.colours;
subpixelorder = p_image.subpixelorder;
size = allocation = p_image.size;
size = p_image.size; // allocation is set in AllocImgBuffer
buffer = 0;
holdbuffer = 0;
AllocImgBuffer(allocation);
AllocImgBuffer(size);
(*fptr_imgbufcpy)(buffer, p_image.buffer, size);
strncpy( text, p_image.text, sizeof(text) );
}
@ -305,7 +305,7 @@ void Image::Initialise()
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) {
unsigned int newsize;
@ -331,7 +331,7 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei
return NULL;
} else {
/* Replace buffer with a bigger one */
DumpImgBuffer();
//DumpImgBuffer(); // Done in AllocImgBuffer too
AllocImgBuffer(newsize);
}
}
@ -369,12 +369,12 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh
unsigned int new_buffer_size = ((p_width*p_height)*p_colours);
if(buffer_size < new_buffer_size) {
Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours );
Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size );
return;
}
if(holdbuffer && buffer) {
if((unsigned int)((p_height*p_width)*p_colours) > allocation) {
if(new_buffer_size > allocation) {
Error("Held buffer is undersized for assigned buffer");
return;
} else {
@ -482,7 +482,7 @@ void Image::Assign( const Image &image ) {
}
} else {
if(new_size > allocation || !buffer) {
DumpImgBuffer();
// DumpImgBuffer(); This is also done in AllocImgBuffer
AllocImgBuffer(new_size);
}
}
@ -1580,7 +1580,11 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres
{
uint8_t *psrc = images[j]->buffer+c;
#ifndef SOLARIS
if ( (unsigned)abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) )
#else
if ( (unsigned)std::abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) )
#endif
{
count++;
}
@ -1593,7 +1597,7 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres
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
{
#ifdef ZM_IMAGE_PROFILING

View File

@ -6,7 +6,6 @@
#include <list>
#include <string>
#include <stdexcept>
#include <iostream>
#include <memory>
#include "zm.h"

View File

@ -32,6 +32,13 @@
#include <stdlib.h>
#include <limits.h>
/* Workaround for GNU/kFreeBSD */
#if defined(__FreeBSD_kernel__)
#ifndef ENODATA
#define ENODATA ENOATTR
#endif
#endif
static unsigned int BigEndian;
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
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_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};
@ -427,7 +434,7 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel,
Panic("Unexpected colours: %d",colours);
}
if( capture ) {
#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0, 8, 0)
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
if(!sws_isSupportedInput(capturePixFormat)) {
Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff));
conversion_type = 2; /* Try ZM format conversions */
@ -615,7 +622,11 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel,
#if HAVE_LIBSWSCALE
/* Initialize swscale stuff */
if(capture && conversion_type == 1) {
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
tmpPicture = av_frame_alloc();
#else
tmpPicture = avcodec_alloc_frame();
#endif
if ( !tmpPicture )
Fatal( "Could not allocate temporary picture" );
@ -845,7 +856,11 @@ void LocalCamera::Initialise()
Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno );
#if HAVE_LIBSWSCALE
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
capturePictures[i] = av_frame_alloc();
#else
capturePictures[i] = avcodec_alloc_frame();
#endif
if ( !capturePictures[i] )
Fatal( "Could not allocate picture" );
avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height );
@ -999,7 +1014,11 @@ void LocalCamera::Initialise()
v4l1_data.buffers[i].height = height;
v4l1_data.buffers[i].format = palette;
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
capturePictures[i] = av_frame_alloc();
#else
capturePictures[i] = avcodec_alloc_frame();
#endif
if ( !capturePictures[i] )
Fatal( "Could not allocate picture" );
avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height );

View File

@ -29,6 +29,9 @@
#ifdef HAVE_LINUX_VIDEODEV_H
#include <linux/videodev.h>
#endif // HAVE_LINUX_VIDEODEV_H
#ifdef HAVE_LIBV4L1_VIDEODEV_H
#include <libv4l1-videodev.h>
#endif // HAVE_LIB4VL1_VIDEODEV_H
#ifdef HAVE_LINUX_VIDEODEV2_H
#include <linux/videodev2.h>
#endif // HAVE_LINUX_VIDEODEV2_H

View File

@ -31,6 +31,9 @@
#include <signal.h>
#include <stdarg.h>
#include <errno.h>
#ifdef __FreeBSD__
#include <sys/thr.h>
#endif
bool Logger::smInitialised = false;
Logger *Logger::smInstance = 0;
@ -527,9 +530,25 @@ void Logger::logPrint( bool hex, const char * const file, const int line, const
#endif
pid_t tid;
#ifdef __FreeBSD__
long lwpid;
thr_self(&lwpid);
tid = lwpid;
if (tid < 0 ) // Thread/Process id
#else
#ifdef HAVE_SYSCALL
#ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
# else
// SOLARIS doesn't have SYS_gettid; don't assume
#ifdef SYS_gettid
if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id
#endif // SYS_gettid
#endif
#endif // HAVE_SYSCALL
#endif
tid = getpid(); // Process id
char *logPtr = logString;

View File

@ -56,6 +56,11 @@
#include <sys/shm.h>
#endif // ZM_MEM_MAPPED
// SOLARIS - we don't have MAP_LOCKED on openSolaris/illumos
#ifndef MAP_LOCKED
#define MAP_LOCKED 0
#endif
//=============================================================================
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
@ -252,7 +257,6 @@ bool Monitor::MonitorLink::hasAlarmed()
else if( shared_data->last_event != (unsigned int)last_event )
{
last_event = shared_data->last_event;
return( true );
}
return( false );
}
@ -442,7 +446,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 ) {
Debug( 1, "Monitor %s has no zones, adding one.", name );
n_zones = 1;
@ -531,14 +535,18 @@ bool Monitor::connect() {
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
return false;
} else {
#ifdef MAP_LOCKED
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 ( errno == EAGAIN ) {
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 );
Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size );
#ifdef MAP_LOCKED
}
}
#endif
if ( mem_ptr == MAP_FAILED )
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
}
@ -3101,7 +3109,7 @@ bool Monitor::closeEvent()
* 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().
*
* 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!
*
* To check black pixels in 32 bit images i can do a more efficient way using

Some files were not shown because too many files have changed in this diff Show More