Skip to content

Instantly share code, notes, and snippets.

@krowe
Last active January 24, 2022 15:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krowe/9e60eb60a8c7fe1de45e to your computer and use it in GitHub Desktop.
Save krowe/9e60eb60a8c7fe1de45e to your computer and use it in GitHub Desktop.

Apache Host Sync

This script is useful for web developers and administrators whom use Apache name based virtual hosts to host multiple websites on the same server and would like to have the Listen and NameVirtualHost directives automatically updated when they add or remove virtual hosts.

Even though these are usually easy to configure it can be tedious to keep these values updated on some setups where hosts are being added and removed frequently. In these setups you could just add these directives to your vhost configuration but, in the case of the Listen directive, you'll need to ensure that they do not conflict so it is more handy to have them all listed in the same place. This is why the default Apache setup breaks these out into a separate file called ports.conf on most setups. Using this script you'll have the best of both worlds by being able to configure only the vhost definition while still having a central definition to avoid conflicts.

It works by scanning a list of configuration files for virtual host definitions and using those to create a list of Listen and NameVirtualHost directives in a ports.conf style Apache configuration file. Depending on how Apache is configured on your system this will mean either just scanning your http.conf file or it may mean scanning your sites-enabled directory on more advanced setups.

You can either configure the script to overwrite your current ports.conf file (if you have one) or you can modify your http.conf file to just include the file that this script generates.

Setup

  1. Get the latest version of this file from the Gist website.
  2. Put the script files anywhere you like. They'll be slightly easier to use if you put them in a path without spaces though.
  3. Edit the script configuration at the top of the script to you preferences. It is heavily commented to assist you.
  4. (Optional) Edit the template file to your needs. This will typically just mean adding any static Listen or NameVirtualHost directives you want.

Usage

The default configuration for this script will prevent it from actually doing anything because there are no search paths defined. You must either uncomment one of the paths provided or provide your own.

It is suggested that you first test this script from the command line until you understand exactly how it works.

NOTE: The script currently takes no arguments because all of the options are in the $cfg array at the top of the file. This may change in future versions but for now you just need to make copies of this script if you want to run multiple setups.

By default this script will save the output file to the same directory as the script. If your file system supports linking, it is suggested that you simply create a link to that file in your Apache configuration directory and then include that link from your http.conf file. If not then you'll want to modify the output_file configuration option to output the file directly to the Apache configuration directory.

<?php /*
This PHP file will search through the Apache configuration file specified and
automatically generate a port configuration file so you don't need to maintain
it separately.
All of the configuration is done in the $ahs_cfg array and the template file which
specifies the basic layout of the generated file.
*/
$path=dirname(__FILE__); // The current file path. It is used so other
// paths can reliably be set relatively.
$ahs_cfg=array(
'output_file'=>"$path/ahs.ports.conf", // This is the file to be generated.
'template_file'=>"$path/ahs.ports.tpl", // The basic file layout which also
// includes the {NAME_VHOST} and
// {LISTEN} replacement strings.
'search'=>array( // This is a list of directories to search for virtual host
// configuration files in. These are glob patterns so you
// may use the wildcards ? and * to specify multiple files
// at once. You can also add as many of these as you like.
# '/etc/httpd/conf/httpd.conf', // This is for RedHat based distros
# '/etc/apache2/sites-enabled/*', // This is for Debian based distros
# 'C:\Program Files\PHP5\http.conf', // This is for Windows
),
'allow_web_access'=>false, // If this is true then this script may be ran
// from a web page. This is not intended for
// production use.
'filter'=>FILTER_FLAG_NO_PRIV_RANGE, // The filter used to detect IPs.
);
############################
define('IS_CLI', PHP_SAPI==='cli');
if(!IS_CLI) echo '<pre>';
echo "Apache Host Sync\n------------------\n\n";
if(IS_CLI==false&&$ahs_cfg['allow_web_access']==false)
die("ERROR: This script may only be ran from the command line.\n");
// echo "Arguments: \n"; print_r(arguments($argv));
if(!isset($ahs_cfg['output_file'])) die("ERROR: Output file not set.\n");
echo "Output File:\n\t{$ahs_cfg['output_file']}\n";
if(!is_writable(dirname($ahs_cfg['output_file'])))
die("ERROR: Cannot write to output file.\n");
if(!isset($ahs_cfg['template_file'])) die(sprintf("ERROR: Template file not set.\n"));
echo "Template File:\n\t{$ahs_cfg['template_file']}\n";
if(!is_readable(dirname($ahs_cfg['template_file'])))
die("ERROR: Cannot read to template file.\n");
if(!isset($ahs_cfg['search'])||!count($ahs_cfg['search']))
die("ERROR: Configuration file search path was not set.\n");
echo "Search Paths:\n";
foreach($ahs_cfg['search'] as $path)
echo "\t$path\n";
echo "\nReading configuration files...\n";
$vhosts=array();
foreach($ahs_cfg['search'] as $path) {
echo ">> $path\n";
$files=glob($path);
foreach($files as $file) {
echo "\t>> ". basename($file) ."\n";
$handle=@fopen($file, "r");
if($handle) {
while(($buffer=fgets($handle, 4096))!==false) {
$status=procLine($buffer);
if(!$status) die("ERROR: Failed reading input line.\n");
if($status===TRUE) continue;
$vhosts[]=$status;
}
if(!feof($handle)) die("ERROR: Unexpected fgets() fail.\n");
fclose($handle);
}
}
}
echo "\n\nGenerating template data...\n";
$nvhost=array();
$listen=array();
foreach($vhosts as $vhost) {
// We're just going to assume that if you have multiple VirtualHost for the
// same port that means you want to use *:port . You could improve this by
// actually checking to see if multiple hosts have been assigned to this
// port but you'd need to rearrange the data a little.
if($vhost['is_name']||isset($nvhost[$vhost['port']])) {
$nvhost[$vhost['port']]='NameVirtualHost *:'.$vhost['port'];
$listen[$vhost['port']]='Listen '.$vhost['port'];
} else {
$nvhost[$vhost['port']]='NameVirtualHost '.$vhost['host'].':'.$vhost['port'];
if($vhost['host']=='*')
$listen[$vhost['port']]='Listen '.$vhost['port'];
else $listen[$vhost['port']]='Listen '.$vhost['host'].':'.$vhost['port'];
}
}
echo "\n\nWriting output file...\n";
$tpl=file_get_contents($ahs_cfg['template_file']);
if($tpl) {
$tpl=str_replace('{NAME_VHOST}', implode("\n", $nvhost), $tpl);
$tpl=str_replace('{LISTEN}', implode("\n", $listen), $tpl);
file_put_contents($ahs_cfg['output_file'], $tpl);
}
echo "\n\nDone.\n";
if(!IS_CLI) echo '</pre>';
############################
function procLine($line) {
if(!preg_match("/^(\w)*<VirtualHost(.)*>(\w)*/", $line)) return true;
$host=substr($line, strpos($line,'VirtualHost')+12, strlen($line)-strrpos($line,'>')+2);
$last_square=strrpos($host, ']'); // For hosts specified in IPv6 format
$cln=strrpos($host, ':');
if($cln!==FALSE && $cln+1<strlen($host) && $cln>$last_square) {
$port=substr($host, $cln+1);
$host=substr($host, 0, $cln);
} else $port='80';
$is_range=strpos($host, '*')!==FALSE;
$is_ip=$is_range||filter_var($host,FILTER_VALIDATE_IP,$ahs_cfg['filter'])!==FALSE;
$is_name=!$is_ip;
return array( 'host'=>$host, 'port'=>$port, 'is_name'=>$is_name,
'is_ip'=>$is_ip, 'is_range'=>$is_range);
}
# This file was generated from a template by ahs.php
{NAME_VHOST}
{LISTEN}
<IfModule mod_ssl.c>
# If you add NameVirtualHost *:443 here, you will also have to change
# the VirtualHost statement in /etc/apache2/sites-available/default-ssl
# to <VirtualHost *:443>
# Server Name Indication for SSL named virtual hosts is currently not
# supported by MSIE on Windows XP.
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment