From 2bc6f1627e2bddd79fb8c4acf0d00f45d02fd82f Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 5 Jun 2017 15:39:19 -0500 Subject: [PATCH] Add support for conf.d subfolder (#1902) * cmake changes to support conf.d * php changes to support conf.d * perl changes to support conf.d * C changes to support conf.d * add conf.d support to rpmspecfile * fix typo * specify ZM_CONFIG_SUBDIR in relevant target distros * put back my config_file perl definition * remove quotes from ZM macros * fix snprintf * add README to conf.d folder * adjust rpm specfile * remove custom permissions from README in rpmspecfile * modify rpm README documentation to reflect conf.d support * set ZM_CONFIG_SUBDIR in debian rules file --- CMakeLists.txt | 15 ++- conf.d/README | 19 ++++ conf.d/zm-server-host.conf | 7 ++ distros/debian/rules | 3 +- distros/redhat/readme/README.Fedora | 55 +++++---- distros/redhat/readme/README.Redhat6 | 50 +++++---- distros/redhat/readme/README.Redhat7 | 59 +++++----- distros/redhat/zoneminder.spec | 8 +- distros/ubuntu1204/rules | 1 + distros/ubuntu1504_cmake_split_packages/rules | 4 +- distros/ubuntu1604/rules | 1 + .../ZoneMinder/lib/ZoneMinder/Config.pm.in | 53 ++++++--- src/zm_config.cpp | 104 ++++++++++++------ src/zm_config.h.in | 3 + web/includes/config.php.in | 56 ++++++++-- zm.conf.in | 17 ++- 16 files changed, 298 insertions(+), 157 deletions(-) create mode 100644 conf.d/README create mode 100644 conf.d/zm-server-host.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index 9737e4d15..95beacca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ mark_as_advanced( ZM_PERL_SEARCH_PATH ZM_TARGET_DISTRO ZM_CONFIG_DIR + ZM_CONFIG_SUBDIR ZM_SYSTEMD) set(ZM_RUNDIR "/var/run/zm" CACHE PATH @@ -137,6 +138,8 @@ set(ZM_WEB_GROUP "" CACHE STRING # Advanced set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH "Location of ZoneMinder configuration, default system config directory") +set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/conf.d" CACHE PATH + "Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d") 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 @@ -177,6 +180,7 @@ if((ZM_TARGET_DISTRO STREQUAL "fc24") OR (ZM_TARGET_DISTRO STREQUAL "fc25")) set(ZM_TMPDIR "/var/lib/zoneminder/temp") set(ZM_LOGDIR "/var/log/zoneminder") set(ZM_CONFIG_DIR "/etc/zm") + set(ZM_CONFIG_SUBDIR "/etc/zm/conf.d") set(ZM_WEBDIR "/usr/share/zoneminder/www") set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") elseif(ZM_TARGET_DISTRO STREQUAL "el6") @@ -185,6 +189,7 @@ elseif(ZM_TARGET_DISTRO STREQUAL "el6") set(ZM_TMPDIR "/var/lib/zoneminder/temp") set(ZM_LOGDIR "/var/log/zoneminder") set(ZM_CONFIG_DIR "/etc/zm") + set(ZM_CONFIG_SUBDIR "/etc/zm/conf.d") set(ZM_WEBDIR "/usr/share/zoneminder/www") set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") elseif(ZM_TARGET_DISTRO STREQUAL "el7") @@ -193,6 +198,7 @@ elseif(ZM_TARGET_DISTRO STREQUAL "el7") set(ZM_TMPDIR "/var/lib/zoneminder/temp") set(ZM_LOGDIR "/var/log/zoneminder") set(ZM_CONFIG_DIR "/etc/zm") + set(ZM_CONFIG_SUBDIR "/etc/zm/conf.d") set(ZM_WEBDIR "/usr/share/zoneminder/www") set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") elseif(ZM_TARGET_DISTRO STREQUAL "OS13") @@ -212,6 +218,7 @@ elseif(ZM_TARGET_DISTRO STREQUAL "FreeBSD") set(ZM_WEB_USER "www") set(ZM_WEB_GROUP "www") set(ZM_CONFIG_DIR "/usr/local/etc/zm") + set(ZM_CONFIG_SUBDIR "/usr/local/etc/zm/conf.d") set(ZM_WEBDIR "/usr/local/share/zoneminder/www") set(ZM_CGIDIR "/usr/local/libexec/zoneminder/cgi-bin") set(ZM_PERL_MM_PARMS "INSTALLDIRS=site") @@ -791,6 +798,11 @@ else(ZM_PERL_SEARCH_PATH) set(EXTRA_PERL_LIB "# Include from system perl paths only") endif(ZM_PERL_SEARCH_PATH) +# 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}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf.d") +endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + # Generate files from the .in files configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY) configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) @@ -837,8 +849,9 @@ else(zmconfgen_result EQUAL 0) "ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}") endif(zmconfgen_result EQUAL 0) -# Install zm.conf +# Install zm.conf and conf.d subfolder install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/conf.d/" DESTINATION "${ZM_CONFIG_SUBDIR}") # Uninstall target configure_file( diff --git a/conf.d/README b/conf.d/README new file mode 100644 index 000000000..dedfcd11f --- /dev/null +++ b/conf.d/README @@ -0,0 +1,19 @@ +conf.d/README + +Any changes to ZoneMinder's configuration should be made here in this folder, +rather than directly editing the default zm.conf file. + +ZoneMinder will process each file in this folder with a ".conf" extension. +Each "Var = Value" pair, in each config file, will be loaded into ZoneMinder's +running configuration, overriding any variables with the same name found in the +default zm.conf file. + +After creating a custom config file, don't forget to set the proper file and +owner permission on it. For example, this is typically what you should do after +saving the config file to disk: + + sudo chown root:apache *.conf + sudo chmod 640 *.conf + +Substitute "apache" with the name of the web server user account on your system. + diff --git a/conf.d/zm-server-host.conf b/conf.d/zm-server-host.conf new file mode 100644 index 000000000..7c61c200d --- /dev/null +++ b/conf.d/zm-server-host.conf @@ -0,0 +1,7 @@ +# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server +# You have been warned +# +# The name specified here must have a corresponding entry +# in the Servers tab under Options +ZM_SERVER_HOST= + diff --git a/distros/debian/rules b/distros/debian/rules index 48a3bdab0..45a64332c 100755 --- a/distros/debian/rules +++ b/distros/debian/rules @@ -22,7 +22,8 @@ override_dh_auto_configure: -DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \ -DZM_WEB_USER=www-data \ -DZM_WEB_GROUP=www-data \ - -DCMAKE_INSTALL_SYSCONFDIR=etc/zm + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_CONFIG_DIR="/etc/zm" override_dh_auto_install: dh_auto_install --buildsystem=cmake diff --git a/distros/redhat/readme/README.Fedora b/distros/redhat/readme/README.Fedora index 626d74df1..e3cca1f8c 100644 --- a/distros/redhat/readme/README.Fedora +++ b/distros/redhat/readme/README.Fedora @@ -1,7 +1,16 @@ What's New ========== -1. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to +1. ZoneMinder now uses a conf.d subfolder to process custom changes to + variables found in zm.conf. Changes to zm.conf will be overwritten + during an upgrade. Instead, create a file with a ".conf" extension under + the conf.d folder and make your changes there. + +2. ZoneMinder now supports recording directly to video container! This feature + is new and should be treated as experimental. Refer to the documentation + regarding how to use this feature. + +3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to "/cgi-bin-zm/zms". This has been to done to avoid this bug: https://bugzilla.redhat.com/show_bug.cgi?id=973067 @@ -9,16 +18,10 @@ What's New and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result in a broken system. You have been warned. -2. Due to the active state of the ZoneMinder project, we now recommend granting - ALL permission to the ZoneMinder mysql account. This change must be done - manually before ZoneMinder will run. See the installation steps below. - -3. This package uses the HTTPS protocol by default to access the web portal. +4. This package uses the HTTPS protocol by default to access the web portal. Requests using HTTP will auto-redirect to HTTPS. See README.https for more information. -4. This package ships with the new ZoneMinder API enabled. - New installs ============ @@ -43,13 +46,17 @@ New installs anything that suits your environment. 3. If you have chosen to change the zoneminder database account credentials to - something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf. - Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous - step. + something other than zmuser/zmpass, you must now create a config file under + /etc/zm/conf.d and set your credentials there. For example, create the file + /etc/zm/conf.d/zm-db-user.conf and add the following content to it: + + ZM_DB_USER = {username of the sql account you want to use} + ZM_DB_PASS = {password of the sql account you want to use} - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. + Once the file has been saved, set proper file & ownership permissions on it: + + sudo chown root:apache *.conf + sudo chmod 640 *.conf 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local timezone. PHP will complain loudly if this is not set, or if it is set @@ -107,21 +114,11 @@ New installs Upgrades ======== -1. Verify /etc/zm/zm.conf. - - If zm.conf was manually edited before running the upgrade, the installation - may not overwrite it. In this case, it will create the file - /etc/zm/zm.conf.rpmnew. - - For example, this will happen if you are using database account credentials - other than zmuser/zmpass. - - Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf - contains any new config settings that may be in zm.conf.rpmnew. - - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. +1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom + changes previously made to zm.conf must now be made in one or more custom + config files, created under the conf.d folder. Do this now. See + /etc/zm/conf.d/README for details. Once you recreate any custom config changes + under the conf.d folder, they will remain in place indefinitely. 2. Verify permissions of the zmuser account. diff --git a/distros/redhat/readme/README.Redhat6 b/distros/redhat/readme/README.Redhat6 index a913f7d76..939300d96 100644 --- a/distros/redhat/readme/README.Redhat6 +++ b/distros/redhat/readme/README.Redhat6 @@ -11,7 +11,16 @@ What's New replacing core packages, such as php, will not be supported by us. You are on your own should you choose to go down that path. -2. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to +2. ZoneMinder now uses a conf.d subfolder to process custom changes to + variables found in zm.conf. Changes to zm.conf will be overwritten + during an upgrade. Instead, create a file with a ".conf" extension under + th2 conf.d folder and make your changes there. + +3. ZoneMinder now supports recording directly to video container! This feature + is new and should be treated as experimental. Refer to the documentation + regarding how to use this feature. + +4. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to "/cgi-bin-zm/zms". This has been to done match the configuration of CentOS7/Fedora and simplify the build process. @@ -19,14 +28,6 @@ What's New Make sure it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result in a broken system. You have been warned. -3. The ZoneMinder configuration file, zm.conf, has been moved to /etc/zm/. - This has been to done match the configuration of CentOS7/Fedora and - simplify the build process. - -4. Due to the active state of the ZoneMinder project, we now recommend granting - ALL permission to the ZoneMinder mysql account. This change must be done - manually before ZoneMinder will run. See the installation steps below. - 5. This package uses the HTTPS protocol by default to access the web portal. Requests using HTTP will auto-redirect to HTTPS. See README.https for more information. @@ -59,9 +60,18 @@ New installs The database account credentials, zmuser/zmpass, are arbitrary. Set them to anything that suits your environment. -3. If you have chosen to change the zoneminder mysql credentials to something - other than zmuser/zmpass then you must now edit /etc/zm/zm.conf. Change - ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step. +3. If you have chosen to change the zoneminder database account credentials to + something other than zmuser/zmpass, you must now create a config file under + /etc/zm/conf.d and set your credentials there. For example, create the file + /etc/zm/conf.d/zm-db-user.conf and add the following content to it: + + ZM_DB_USER = {username of the sql account you want to use} + ZM_DB_PASS = {password of the sql account you want to use} + + Once the file has been saved, set proper file & ownership permissions on it: + + sudo chown root:apache *.conf + sudo chmod 640 *.conf 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local timezone. PHP will complain loudly if this is not set, or if it is set @@ -110,17 +120,11 @@ New installs Upgrades ======== -1. Verify /etc/zm/zm.conf. - - If zm.conf was manually edited before running the upgrade, the installation - may not overwrite it. In this case, it will create the file - /etc/zm/zm.conf.rpmnew. - - For example, this will happen if you are using database account credentials - other than zmuser/zmpass. - - Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf - contains any new config settings that may be in zm.conf.rpmnew. +1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom + changes previously made to zm.conf must now be made in one or more custom + config files, created under the conf.d folder. Do this now. See + /etc/zm/conf.d/README for details. Once you recreate any custom config changes + under the conf.d folder, they will remain in place indefinitely. 2. Verify permissions of the zmuser account. diff --git a/distros/redhat/readme/README.Redhat7 b/distros/redhat/readme/README.Redhat7 index fd3b87cc4..8817b925c 100644 --- a/distros/redhat/readme/README.Redhat7 +++ b/distros/redhat/readme/README.Redhat7 @@ -1,23 +1,26 @@ What's New ========== -1. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to +1. ZoneMinder now uses a conf.d subfolder to process custom changes to + variables found in zm.conf. Changes to zm.conf will be overwritten + during an upgrade. Instead, create a file with a ".conf" extension under + the conf.d folder and make your changes there. + +2. ZoneMinder now supports recording directly to video container! This feature + is new and should be treated as experimental. Refer to the documentation + regarding how to use this feature. + +3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to "/cgi-bin-zm/zms". This has been to done to avoid this bug: https://bugzilla.redhat.com/show_bug.cgi?id=973067 - IMPORTANT: You must manually verify the value of PATH_ZMS under Options. - Make sure it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result + IMPORTANT: You must manually inspect the value for PATH_ZMS under Options + and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result in a broken system. You have been warned. -2. Due to the active state of the ZoneMinder project, we now recommend granting - ALL permission to the ZoneMinder mysql account. This change must be done - manually before ZoneMinder will run. See the installation steps below. - -3. This package uses the HTTPS protocol by default to access the web portal. +4. This package uses the HTTPS protocol by default to access the web portal. Requests using HTTP will auto-redirect to HTTPS. See README.https for more information. - -4. This package ships with the new ZoneMinder API enabled. New installs ============ @@ -43,13 +46,17 @@ New installs anything that suits your environment. 3. If you have chosen to change the zoneminder database account credentials to - something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf. - Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous - step. + something other than zmuser/zmpass, you must now create a config file under + /etc/zm/conf.d and set your credentials there. For example, create the file + /etc/zm/conf.d/zm-db-user.conf and add the following content to it: + + ZM_DB_USER = {username of the sql account you want to use} + ZM_DB_PASS = {password of the sql account you want to use} - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. + Once the file has been saved, set proper file & ownership permissions on it: + + sudo chown root:apache *.conf + sudo chmod 640 *.conf 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local timezone. PHP will complain loudly if this is not set, or if it is set @@ -98,21 +105,11 @@ New installs Upgrades ======== -1. Verify /etc/zm/zm.conf. - - If zm.conf was manually edited before running the upgrade, the installation - may not overwrite it. In this case, it will create the file - /etc/zm/zm.conf.rpmnew. - - For example, this will happen if you are using database account credentials - other than zmuser/zmpass. - - Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf - contains any new config settings that may be in zm.conf.rpmnew. - - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. +1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom + changes previously made to zm.conf must now be made in one or more custom + config files, created under the conf.d folder. Do this now. See + /etc/zm/conf.d/README for details. Once you recreate any custom config changes + under the conf.d folder, they will remain in place indefinitely. 2. Verify permissions of the zmuser account. diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index dad18d307..4eab2a429 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -280,7 +280,13 @@ rm -rf %{_docdir}/%{name}-%{version} %files %license COPYING %doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc -%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf +%dir %{_sysconfdir}/zm +%dir %{_sysconfdir}/zm/conf.d +%{_sysconfdir}/zm/conf.d/README +# Always overwrite zm.conf now that ZoneMinder supports conf.d folder +%attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf +%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf + %config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf %config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules index 2b54a2131..1f7755c45 100755 --- a/distros/ubuntu1204/rules +++ b/distros/ubuntu1204/rules @@ -20,6 +20,7 @@ override_dh_auto_configure: -DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_BUILD_TYPE=Release \ -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_RUNDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \ -DZM_TMPDIR="/tmp/zm" \ diff --git a/distros/ubuntu1504_cmake_split_packages/rules b/distros/ubuntu1504_cmake_split_packages/rules index a534e8089..8a2c2643e 100755 --- a/distros/ubuntu1504_cmake_split_packages/rules +++ b/distros/ubuntu1504_cmake_split_packages/rules @@ -62,7 +62,9 @@ override_dh_auto_configure: -DZM_CGIDIR=/usr/lib/cgi-bin \ -DZM_WEB_USER=www-data \ -DZM_WEB_GROUP=www-data \ - -DCMAKE_INSTALL_SYSCONFDIR=etc/zm + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_CONFIG_DIR="/etc/zm" + override_dh_auto_test: # do not run tests... diff --git a/distros/ubuntu1604/rules b/distros/ubuntu1604/rules index e3b235be8..c91790d68 100755 --- a/distros/ubuntu1604/rules +++ b/distros/ubuntu1604/rules @@ -20,6 +20,7 @@ override_dh_auto_configure: -DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_BUILD_TYPE=Release \ -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_RUNDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \ -DZM_TMPDIR="/tmp/zm" \ diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index 2ba12dfa2..60cdce658 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -65,26 +65,28 @@ our $VERSION = $ZoneMinder::Base::VERSION; use constant ZM_PID => "@ZM_PID@"; # Path to the ZoneMinder run pid file use constant ZM_CONFIG => "@ZM_CONFIG@"; # Path to the ZoneMinder config file +use constant ZM_CONFIG_SUBDIR => "@ZM_CONFIG_SUBDIR@"; # Path to the ZoneMinder config subfolder use Carp; # Load the config from the database into the symbol table BEGIN { + + # Process name, value pairs from the main config file first my $config_file = ZM_CONFIG; - 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; + process_configfile($config_file); + + # Search for user created config files. If one or more are found then + # update the Config hash with those values + if ( -d ZM_CONFIG_SUBDIR ) { + if ( -R ZM_CONFIG_SUBDIR ) { + foreach my $filename ( glob ZM_CONFIG_SUBDIR."/*.conf" ) { + process_configfile($filename); + } + } else { + print( STDERR "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on ".ZM_CONFIG_SUBDIR.".\n" ); + } } - close( $CONFIG ); use DBI; my $socket; @@ -126,6 +128,31 @@ BEGIN { } $sth->finish(); } + + # This subroutine must be inside the BEGIN block + sub process_configfile { + my $config_file = shift; + + if ( -R $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 ); + } else { + print( STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n" ); + } + } + } # end BEGIN sub loadConfigFromDB { diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 61c61bf5a..eedebaf45 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -24,14 +24,80 @@ #include #include #include +#include +#include #include "zm_utils.h" void zmLoadConfig() { + + // Process name, value pairs from the main config file first + char configFile[PATH_MAX] = ZM_CONFIG; + process_configfile(configFile); + + // Search for user created config files. If one or more are found then + // update the Config hash with those values + DIR* configSubFolder = opendir(ZM_CONFIG_SUBDIR); + if ( configSubFolder ) { // subfolder exists and is readable + char glob_pattern[PATH_MAX] = ""; + snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.conf", ZM_CONFIG_SUBDIR ); + + glob_t pglob; + int glob_status = glob( glob_pattern, 0, 0, &pglob ); + if ( glob_status != 0 ) { + if ( glob_status < 0 ) { + Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); + } else { + Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); + } + } else { + for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) { + process_configfile(pglob.gl_pathv[i]); + } + closedir(configSubFolder); + } + globfree( &pglob ); + } + + zmDbConnect(); + config.Load(); + config.Assign(); + + // Populate the server config entries + if ( ! staticConfig.SERVER_ID ) { + if ( ! staticConfig.SERVER_NAME.empty() ) { + + Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); + std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); + if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { + staticConfig.SERVER_ID = atoi(dbrow[0]); + } else { + Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); + } + + } // end if has SERVER_NAME + } else if ( staticConfig.SERVER_NAME.empty() ) { + Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); + std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); + + if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { + staticConfig.SERVER_NAME = std::string(dbrow[0]); + } else { + Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); + } + if ( staticConfig.SERVER_ID ) { + Debug( 3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID ); + } else { + Debug( 3, "Single server configuration assumed because no Server ID or Name was specified." ); + } + } +} + +void process_configfile( char* configFile) { FILE *cfg; char line[512]; - if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) { - Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); + if ( (cfg = fopen( configFile, "r")) == NULL ) { + Fatal( "Can't open %s: %s", configFile, strerror(errno) ); } while ( fgets( line, sizeof(line), cfg ) != NULL ) { char *line_ptr = line; @@ -58,7 +124,7 @@ void zmLoadConfig() { // Now look for the '=' in the middle of the line temp_ptr = strchr( line_ptr, '=' ); if ( !temp_ptr ) { - Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); + Warning( "Invalid data in %s: '%s'", configFile, line ); continue; } @@ -99,38 +165,6 @@ void zmLoadConfig() { } } // end foreach line of the config fclose( cfg ); - zmDbConnect(); - config.Load(); - config.Assign(); - - // Populate the server config entries - if ( ! staticConfig.SERVER_ID ) { - if ( ! staticConfig.SERVER_NAME.empty() ) { - - Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); - std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); - if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { - staticConfig.SERVER_ID = atoi(dbrow[0]); - } else { - Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); - } - - } // end if has SERVER_NAME - } else if ( staticConfig.SERVER_NAME.empty() ) { - Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); - std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); - - if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { - staticConfig.SERVER_NAME = std::string(dbrow[0]); - } else { - Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); - } - if ( staticConfig.SERVER_ID ) { - Debug( 3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID ); - } else { - Debug( 3, "Single server configuration assumed because no Server ID or Name was specified." ); - } - } } StaticConfig staticConfig; diff --git a/src/zm_config.h.in b/src/zm_config.h.in index 919cb0c82..49b983fa7 100644 --- a/src/zm_config.h.in +++ b/src/zm_config.h.in @@ -26,6 +26,7 @@ #include #define ZM_CONFIG "@ZM_CONFIG@" // Path to config file +#define ZM_CONFIG_SUBDIR "@ZM_CONFIG_SUBDIR@" // Path to the ZoneMinder config subfolder #define ZM_VERSION "@VERSION@" // ZoneMinder Version #define ZM_HAS_V4L1 @ZM_HAS_V4L1@ @@ -58,6 +59,8 @@ extern void zmLoadConfig(); +extern void process_configfile( char* configFile ); + struct StaticConfig { std::string DB_HOST; diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 0af60b76a..991414a56 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -22,6 +22,7 @@ // This section contains options substituted by the zmconfig.pl utility, do not edit these directly // define( "ZM_CONFIG", "@ZM_CONFIG@" ); // Path to config file +define( "ZM_CONFIG_SUBDIR", "@ZM_CONFIG_SUBDIR@" ); // Path to config subfolder // Define, and override any given in config file define( "ZM_VERSION", "@VERSION@" ); // Version define( "ZM_DIR_TEMP", "@ZM_TMPDIR@" ); @@ -36,19 +37,28 @@ if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 ) error_log( "Warning, overriding installed $localConfigFile file with local copy" ); $configFile = $localConfigFile; } - -$cfg = fopen( $configFile, "r") or die("Could not open config file."); -while ( !feof($cfg) ) -{ - $str = fgets( $cfg, 256 ); - if ( preg_match( '/^\s*$/', $str )) - continue; - elseif ( preg_match( '/^\s*#/', $str )) - continue; - elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) - define( $matches[1], $matches[2] ); + +# Process name, value pairs from the main config file first +$configvals = process_configfile($configFile); + +# Search for user created config files. If one or more are found then +# update our config value array with those values +$configSubFolder = ZM_CONFIG_SUBDIR; +if ( is_dir($configSubFolder) ) { + if ( is_readable($configSubFolder) ) { + foreach ( glob("$configSubFolder/*.conf") as $filename ) { + $configvals = array_replace($configvals, process_configfile($filename) ); + } + } else { + error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." ); + } +} + +# Now that our array our finalized, define each key => value +# pair in the array as a constant +foreach( $configvals as $key => $value) { + define( $key, $value ); } -fclose( $cfg ); // // This section is options normally derived from other options or configuration @@ -189,5 +199,27 @@ if ( ! defined('ZM_SERVER_ID') ) { } } +function process_configfile($configFile) { + if ( is_readable( $configFile ) ) { + $configvals = array(); + + $cfg = fopen( $configFile, "r") or die("Could not open config file."); + while ( !feof($cfg) ) + { + $str = fgets( $cfg, 256 ); + if ( preg_match( '/^\s*$/', $str )) + continue; + elseif ( preg_match( '/^\s*#/', $str )) + continue; + elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) + $configvals[$matches[1]] = $matches[2]; + } + fclose( $cfg ); + return( $configvals ); + } else { + error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." ); + return( false ); + } +} ?> diff --git a/zm.conf.in b/zm.conf.in index 73e4a3d9c..296078dde 100644 --- a/zm.conf.in +++ b/zm.conf.in @@ -1,13 +1,16 @@ # ========================================================================== # -# ZoneMinder Base Configuration, $Date$, $Revision$ +# ZoneMinder Base Configuration File # # ========================================================================== # -# This file is generated by 'configure'. Care should be taken if manually -# editing this file as an changes may be overwritten by subsequent configuration -# or installations. +# *** DO NOT EDIT THIS FILE *** +# Changes made directly to this configuration file are no longer supported. +# They will be overwritten during an upgrade. # +# Instead, create a custom configuration file, with an extention of ".conf" +# under the @ZM_CONFIG_SUBDIR@ subfolder containing your desired modifications. +# # Path to installed data directory, used mostly for finding DB upgrade scripts ZM_PATH_DATA=@PKGDATADIR@ @@ -47,9 +50,3 @@ ZM_DB_USER=@ZM_DB_USER@ # ZoneMinder database password ZM_DB_PASS=@ZM_DB_PASS@ -# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server -# You have been warned -# -# The name specified here must have a corresponding entry -# in the Servers tab under Options -ZM_SERVER_HOST=