diff --git a/docs/installationguide/images/zm-multiserver.png b/docs/installationguide/images/zm-multiserver.png
new file mode 100644
index 000000000..360b612a8
Binary files /dev/null and b/docs/installationguide/images/zm-multiserver.png differ
diff --git a/docs/installationguide/images/zm-multiserver.xml b/docs/installationguide/images/zm-multiserver.xml
new file mode 100644
index 000000000..c829e1a94
--- /dev/null
+++ b/docs/installationguide/images/zm-multiserver.xml
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/installationguide/index.rst b/docs/installationguide/index.rst
new file mode 100644
index 000000000..be0df2c06
--- /dev/null
+++ b/docs/installationguide/index.rst
@@ -0,0 +1,13 @@
+Installation Guide
+======================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ ubuntu
+ debian
+ fedora
+ centos
+ multiserver
diff --git a/docs/installationguide/multiserver.rst b/docs/installationguide/multiserver.rst
new file mode 100644
index 000000000..aa6139551
--- /dev/null
+++ b/docs/installationguide/multiserver.rst
@@ -0,0 +1,57 @@
+Multi-Server Install
+====================
+
+It is possible to run multiple ZoneMinder servers and manage them from a single interface. To achieve this each zoneminder server is connected to a single shared database server and shares file storage for event data.
+
+.. image:: images/zm-multiserver.png
+
+Topology Design Notes
+---------------------
+
+1. Device symbols represent separate logical functions, not necessarily separate hardware. For example, the Database Server and a ZoneMinder Server, can reside on the same physical hardware.
+
+2. Configure each ZoneMinder Server to use the same, remote Database Server (Green).
+
+3. The Storage Server (Red) represents shared storage, accessible by all ZoneMinder Servers, mounted under each server’s events folder.
+
+4. Create at least two networks for best performance. Dedicate a Storage LAN for communication with the Storage and Database Servers. Make use of multipath and jumbo frames if possible. Keep all other traffic off the Storage LAN! Dedicate the second LAN, called the Video LAN in the diagram, for all other traffic.
+
+New installs
+------------
+
+1. Follow the normal instructions for your distro for installing ZoneMinder onto all the ZoneMinder servers in the normal fashion. Only a single database will be needed either as standalone, or on one of the ZoneMinder Servers.
+
+2. On each ZoneMinder server, edit zm.conf. Find the ZM_DB_HOST variable and set it to the name or ip address of your Database Server.
+
+3. Copy the file /usr/share/zoneminder/db/zm_create.sql from one of the ZoneMinder Servers to the machine targeted as the Database Server.
+
+4. Install mysql/mariadb server onto the Database Server.
+
+5. It is advised to run "mysql_secure_installation" to help secure the server.
+
+6. Using the password for the root account set during the previous step, create the ZoneMinder database and configure a database account for ZoneMinder to use:
+
+::
+
+ mysql -u root -p < zm_create.sql
+ mysql -u root -p
+ mysql> grant all on zm.* to 'zmuser'@'%' identified by 'zmpass';
+ mysql> exit;
+ mysqladmin -u root -p reload
+
+The database account credentials, zmuser/zmpass, are arbitrary. Set them to anything that suits your environment.
+Note that these commands are just an example and might not be secure enough for your environment.
+
+7. If you have chosen to change the ZoneMinder database account credentials to something other than zmuser/zmpass, you must now update zm.conf on each ZoneMinder Server. Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step.
+
+Additionally, you must also edit /usr/share/zoneminder/www/api/app/Config/database.php in a similar manner on each ZoneMinder Server. Scroll down and change login and password to the values you created in the previous step.
+
+8. All ZoneMinders Servers must share a common events folder. This can be done in any manner supported by the underlying operating system. From the Storage Server, share/export a folder to be used for ZoneMinder events.
+
+9. From each ZoneMinder Server, mount the shared events folder on the Storage Server to the events folder on the local ZoneMinder Server.
+
+NOTE: The location of this folder varies by distro. This folder is often found under "/var/lib/zoneminder/events" for RedHat based distros and "/var/cache/zoneminder/events" for Debain based distros. This folder is NOT a Symbolic Link!
+
+10. Open your browser and point it to the web console on any of the ZoneMinder Servers (they will all be the same). Open Options, click the Servers tab,and populate this screen with all of your ZoneMinder Servers.
+
+11. When creating a new Monitor, remember to select the server the camera will be assigned to from the Server drop down box.
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in
index 3998f650d..c3bc792aa 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in
@@ -114,6 +114,7 @@ BEGIN
my $result = $sth->fetchrow_hashref();
$Config{ZM_SERVER_ID} = $$result{Id};
}
+ $Config{ZM_SERVER_ID} = undef;
}
}
diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in
index be563118b..183f8084c 100644
--- a/scripts/zmpkg.pl.in
+++ b/scripts/zmpkg.pl.in
@@ -226,7 +226,7 @@ if ( $command =~ /^(?:start|restart)$/ )
zmMemTidy();
runCommand( "zmdc.pl startup" );
- Info( "Starting up services for server $Config{ZM_SERVER_ID}\n" );
+ Info( "Starting up services" . ( $Config{ZM_SERVER_ID} ? " for server $Config{ZM_SERVER_ID}\n" : "\n" ) );
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
diff --git a/web/ajax/log.php b/web/ajax/log.php
index 1e565424c..a250c952d 100644
--- a/web/ajax/log.php
+++ b/web/ajax/log.php
@@ -73,7 +73,7 @@ switch ( $_REQUEST['task'] )
$logs = array();
foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) {
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
- $log['Server'] = $log['ServerId'] ? $servers_by_Id[$log['ServerId']]->Name() : '';
+ $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$logs[] = $log;
}
$options = array();
@@ -107,7 +107,7 @@ switch ( $_REQUEST['task'] )
elseif ( $field == 'ServerId' )
{
foreach( dbFetchAll( $sql, $field, array_values($fieldValues) ) as $value )
- $options['ServerId'][$value] = $value ? $servers_by_Id[$value]->Name() : '';
+ $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : '';
}
else
@@ -216,6 +216,7 @@ switch ( $_REQUEST['task'] )
foreach ( dbFetchAll( $sql, NULL, $values ) as $log )
{
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
+ $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$logs[] = $log;
}
switch( $format )
@@ -246,7 +247,7 @@ switch ( $_REQUEST['task'] )
)."\n" );
foreach ( $logs as $log )
{
- fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $servers_by_Id[$log['ServerId']]->Name(),$log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
+ fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
}
break;
}
@@ -306,7 +307,7 @@ tr.log-dbg td {
elseif ( $classLevel > Logger::DEBUG )
$classLevel = Logger::DEBUG;
$logClass = 'log-'.strtolower(Logger::$codes[$classLevel]);
- fprintf( $exportFP, "
%s | %s | %s | %d | %s | %s | %s | %s |
\n", $logClass, $log['DateTime'], $log['Component'], $servers_by_Id[$log['ServerId']]->Name(), $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
+ fprintf( $exportFP, " %s | %s | %s | %d | %s | %s | %s | %s |
\n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
}
fwrite( $exportFP,
'