diff options
Diffstat (limited to 'vhostcname/vhostcname')
-rwxr-xr-x | vhostcname/vhostcname | 744 |
1 files changed, 557 insertions, 187 deletions
diff --git a/vhostcname/vhostcname b/vhostcname/vhostcname index 359101c..fc6dd2d 100755 --- a/vhostcname/vhostcname +++ b/vhostcname/vhostcname @@ -1,3 +1,3 @@ #!/usr/bin/perl -# Copyright (C) 2014 Sergey Poznyakoff <gray@gnu.org> +# Copyright (C) 2014-2016 Sergey Poznyakoff <gray@gnu.org> # @@ -24,26 +24,31 @@ use Net::DNS; -my $script; # This script name; - +my $progname; # This script name; +my $progdescr = "update DNS from Apache virtual host configuration"; my $config_file = "/etc/vhostcname.conf"; +my %config = ( + core => { + 'cache' => "/var/run/vhostcname.cache", +# Default TTL. + 'ttl' => 3600, +# A globbing pattern for Apache configuration files. + 'apache-config-pattern' => "*", + } +); + +use constant EX_OK => 0; +use constant EX_NOTUPDATED => 1; +use constant EX_USAGE => 64; +use constant EX_NOINPUT => 66; +use constant EX_CANTCREAT => 73; +use constant EX_CONFIG => 78; -my $cnamelist = "/var/run/vhostcname.cache"; my $host; # This host name. -my @zone; # List of acceptable DNS zones. my $nameserver; # Nameserver to use for updates. -my @tsig_args; # Arguments to sing_tsig (path to the DNSSEC key file, or - # the key name and hash. -my $ttl = 3600; # Default TTL. -my $confdir; # Apache configuration directory. -my $confpat = "*"; # A globbing pattern for Apache configuration files. my $dry_run; # Dry-run mode. my $debug; # Debug level. -my $allow_wildcard_domains; - -my $help; # Display help summary. -my $man; # Ditto in manpage format. -my $status = 0; # Default exit status. +my $status = EX_OK;# Default exit status. sub err { - print STDERR "$script: "; + print STDERR "$progname: "; print STDERR $_ for (@_); @@ -58,21 +63,189 @@ sub abend { -sub read_config_file($) { - my $file = shift; - unless (-f $file) { - print STDERR "$script: configuration file $file does not exist\n" - if ($debug); - return; +sub parse_section { + my ($conf, $input) = @_; + my $ref = $conf; + my $quote; + my $rootname; + while ($input ne '') { + my $name; + if (!defined($quote)) { + if ($input =~ /^"(.*)/) { + $quote = ''; + $input = $1; + } elsif ($input =~ /^(.+?)(?:\s+|")(.*)/) { + $name = $1; + $input = $2; + } else { + $name = $input; + $input = ''; + } + } else { + if ($input =~ /^([^\\"]*)\\(.)(.*)/) { + $quote .= $1 . $2; + $input = $3; + } elsif ($input =~ /^([^\\"]*)"\s*(.*)/) { + $name = $quote . $1; + $input = $2; + $quote = undef; + } else { + die "unparsable input $input"; + } + } + + if (defined($name)) { + $rootname = $name unless defined $rootname; + $ref->{$name} = {} unless ref($ref->{$name}) eq 'HASH'; + $ref = $ref->{$name}; + $name = undef; } - print STDERR "$script: reading $file\n" if ($debug); - open(my $fd, "<", $file) or abend(1, "cannot open $file: $!"); + } + return ($ref, $rootname); +} + +sub check_mandatory { + my ($section, $kw, $loc, $s) = @_; + my $err = 0; + while (my ($k, $d) = each %{$kw}) { + if (ref($d) eq 'HASH' + and $d->{mandatory} + and !exists($section->{$k})) { + if (exists($d->{section})) { + if ($s) { + err("$loc: mandatory section [$k] not present"); + ++$err; + } + } else { + err("$loc: mandatory variable \"$k\" not set"); + ++$err; + } + } + } + return $err; +} + +sub readconfig { + my $file = shift; + my $conf = shift; + my %param = @_; + +# debug(2, "reading $file"); + open(my $fd, "<", $file) + or do { + err("can't open configuration file $file: $!"); + return 1 if $param{include}; + exit(EX_NOINPUT); + }; + + my $line; + my $err; + my $section = $conf; + my $kw = $param{kw}; + my $include = 0; + my $rootname; + while (<$fd>) { + ++$line; chomp; + if (/\\$/) { + chop; + $_ .= <$fd>; + redo; + } + s/^\s+//; s/\s+$//; - s/\s+=\s+/=/; s/#.*//; next if ($_ eq ""); - unshift(@ARGV, "--$_"); + + if (/^\[(.+?)\]$/) { + $include = 0; + my $arg = $1; + $arg =~ s/^\s+//; + $arg =~ s/\s+$//; + if ($arg eq 'include') { + $include = 1; + } else { + ($section, $rootname) = parse_section($conf, $1); + if (ref($param{kw}) eq 'HASH') { + if (defined($rootname)) { + if (ref($param{kw}{$rootname}) eq 'HASH' + and exists($param{kw}{$rootname}{section})) { + $kw = $param{kw}{$rootname}{section}; + } else { + err("$file:$line: unknown section"); + $kw = undef; + } + } else { + $kw = $param{kw}; + } } - close($fd); + } + } elsif (/([\w_-]+)\s*=\s*(.*)/) { + my ($k, $v) = ($1, $2); + $k = lc($k) if $param{ci}; + + if ($include) { + if ($k eq 'path') { + $err += readconfig($v, $conf, include => 1, @_); + } elsif ($k eq 'pathopt') { + $err += readconfig($v, $conf, include => 1, @_) + if -f $v; + } elsif ($k eq 'glob') { + foreach my $file (bsd_glob($v, 0)) { + $err += readconfig($file, $conf, include => 1, @_); + } + } else { + err("$file:$line: unknown keyword"); + ++$err; + } + next; + } + + if (defined($kw)) { + my $x = $kw->{$k}; + if (!defined($x)) { + err("$file:$line: unknown keyword $k"); + ++$err; + next; + } elsif (ref($x) eq 'HASH') { + if (exists($x->{re})) { + if ($v !~ /$x->{re}/) { + err("$file:$line: invalid value for $k"); + ++$err; + next; + } + if (exists($x->{check}) + and !&{$x->{check}}($k, $v, "$file:$line")) { + ++$err; + next; + } + } elsif (exists($x->{check})) { + if (!&{$x->{check}}($k, $v, "$file:$line")) { + ++$err; + next; + } + } elsif (!exists($x->{var}) and + !exists($x->{parser}) and + !exists($x->{mandatory})) { + err("$file:$line: unknown keyword $k"); + ++$err; + next; + } + if (exists($x->{parser}) + and !&{$x->{parser}}($k, \$v, "$file:$line")) { + ++$err; + next; + } + } + } + + $section->{$k} = $v; + } else { + err("$file:$line: malformed line"); + ++$err; + next; + } + } + close $fd; + exit(EX_CONFIG) if $err; } @@ -85,3 +258,3 @@ sub valid_domain_name { my $name = shift; - $name =~ s/^\*\.// if ($allow_wildcard_domains); + $name =~ s/^\*\.// if ($config{core}{'allow-wildcards'}); foreach my $label (split(/\./, $name)) { @@ -99,5 +272,5 @@ sub get_cnames($) { - foreach my $file (glob "$dir/$confpat") { + foreach my $file (glob "$dir/$config{core}{'apache-config-pattern'}") { next unless (-f $file); - print STDERR "$script: reading cnames from $file\n" if ($debug > 2); + print STDERR "$progname: reading cnames from $file\n" if ($debug > 2); @@ -116,9 +289,9 @@ sub get_cnames($) { unless (valid_domain_name($name)) { - print STDERR "$script: $file:$line: $name: invalid domain name\n"; + print STDERR "$progname: $file:$line: $name: invalid domain name\n"; next; } - foreach my $z (@zone) { + foreach my $z (keys %{$config{zone}}) { if ($name =~ /.*\.$z$/) { if ($name =~ /^\*\.(.+)/ and $1 eq $z) { - print STDERR "$script: $file:$line: $name: first-level wildcard\n"; + print STDERR "$progname: $file:$line: $name: first-level wildcard\n"; next; @@ -143,3 +316,3 @@ sub read_cname_list($) { if (-f $file) { - open(my $fd, "<", $file) or abend(1, "cannot open $file: $!"); + open(my $fd, "<", $file) or abend(EX_NOINPUT, "cannot open $file: $!"); while (<$fd>) { @@ -163,3 +336,4 @@ sub write_cname_list { - open(my $fd, ">", $file) or abend(1, "cannot open $file for writing: $!"); + open(my $fd, ">", $file) or + abend(EX_CANTCREAT, "cannot open $file for writing: $!"); foreach my $h (sort keys %hash) { @@ -171,3 +345,2 @@ sub write_cname_list { sub ns_update { - my $resolver = shift; my $name = shift; @@ -177,3 +350,5 @@ sub ns_update { - print STDERR "$script: updating $name in $domain: ". + my $resolver = get_zone_resolver($domain); + + print STDERR "$progname: updating $name in $domain: ". join(',', map { "$_ => $hash{$_}" } keys %hash) . @@ -191,3 +366,3 @@ sub ns_update { } - $update->sign_tsig(@tsig_args) if ($#tsig_args >= 0); + zone_sign_tsig($update, $domain); my $reply = $resolver->send($update); @@ -195,5 +370,5 @@ sub ns_update { if ($reply->header->rcode eq 'NOERROR') { - print STDERR "$script: update successful\n" if ($debug>3); + print STDERR "$progname: update successful\n" if ($debug>3); } elsif ($ignorerr{$reply->header->rcode}) { - print STDERR "$script: ignoring " . $reply->header->rcode . ': ' . + print STDERR "$progname: ignoring " . $reply->header->rcode . ': ' . join(',', map { "$_ => $hash{$_}" } keys %hash) . "\n" @@ -205,3 +380,3 @@ sub ns_update { $reply->header->rcode); - $status = 2; + $status = EX_NOTUPDATED; return 0; @@ -213,3 +388,3 @@ sub ns_update { $resolver->errorstring); - $status = 2; + $status = EX_NOTUPDATED; return 0; @@ -219,2 +394,33 @@ sub ns_update { +sub get_zone_resolver { + my $zone = shift; + unless (defined($config{zone}{$zone}{resolver})) { + my $resolver = new Net::DNS::Resolver; + $resolver->nameservers($config{zone}{$zone}{server}) + if defined $config{zone}{$zone}{server}; + $config{zone}{$zone}{resolver} = $resolver; + } + return $config{zone}{$zone}{resolver}; +} + +sub zone_sign_tsig { + my ($update, $zone) = @_; + my @tsig_args; + + my $zcfg = $config{zone}{$zone}; + if (exists($zcfg->{'ns-key-file'})) { + push @tsig_args, split(/\s+/, $zcfg->{'ns-key-file'}); + } elsif (exists($zcfg->{'ns-key'})) { + push @tsig_args, @{$zcfg->{'ns-key'}}; + } + if ($#tsig_args == -1) { + if (exists($config{core}{'ns-key-file'})) { + push @tsig_args, split(/\s+/, $config{core}{'ns-key-file'}); + } elsif (exists($config{core}{'ns-key'})) { + push @tsig_args, @{$config{core}{'ns-key'}}; + } + } + $update->sign_tsig(@tsig_args) if ($#tsig_args >= 0); +} + sub update_cnames_from_hash { @@ -222,8 +428,8 @@ sub update_cnames_from_hash { - print STDERR "$script: " . keys(%hash) . " names to update\n" + print STDERR "$progname: " . keys(%hash) . " names to update\n" if ($debug > 2); - my %oldhash = read_cname_list($cnamelist); + my %oldhash = read_cname_list($config{core}{cache}); my @namelist = sort(keys(%hash)); - if (join(",", @namelist) eq join(".", sort(keys(%oldhash)))) { - print STDERR "$script: nothing to update\n" if ($debug); + if (join(",", @namelist) eq join(",", sort(keys(%oldhash)))) { + print STDERR "$progname: nothing to update\n" if ($debug); return; @@ -231,5 +437,2 @@ sub update_cnames_from_hash { - my $resolver = new Net::DNS::Resolver; - $resolver->nameservers($nameserver) if defined($nameserver); - my $name; @@ -239,3 +442,3 @@ sub update_cnames_from_hash { } else { - ns_update($resolver, $name, $hash{$name}, + ns_update($name, $hash{$name}, prereq => yxdomain($name), @@ -243,6 +446,6 @@ sub update_cnames_from_hash { ignore => 'NXDOMAIN'); - print STDERR "$script: $name $ttl CNAME $host\n" if ($debug); + print STDERR "$progname: $name $config{core}{ttl} CNAME $config{core}{hostname}\n" if ($debug); delete $hash{$name} - unless ns_update($resolver, $name, $hash{$name}, - update => rr_add("$name $ttl CNAME $host")); + unless ns_update($name, $hash{$name}, + update => rr_add("$name $config{core}{ttl} CNAME $config{core}{hostname}")); } @@ -251,3 +454,3 @@ sub update_cnames_from_hash { foreach $name (keys %oldhash) { - ns_update($resolver, $name, $oldhash{$name}, + ns_update($name, $oldhash{$name}, prereq => yxrrset("$name CNAME"), @@ -257,3 +460,3 @@ sub update_cnames_from_hash { - write_cname_list($cnamelist, %hash); + write_cname_list($config{core}{cache}, %hash); } @@ -265,13 +468,10 @@ sub update_cnames_from_dir($) { sub nscleanup { - print STDERR "$script: Removing DNS CNAME records\n" if ($debug); - - my $resolver = new Net::DNS::Resolver; - $resolver->nameservers($nameserver) if defined($nameserver); + print STDERR "$progname: Removing DNS CNAME records\n" if ($debug); - my %hash = read_cname_list($cnamelist); + my %hash = read_cname_list($config{core}{cache}); foreach my $name (keys %hash) { - print STDERR "$script: removing $name from $hash{$name}\n" + print STDERR "$progname: removing $name from $hash{$name}\n" if ($debug); delete $hash{$name} - if ns_update($resolver, $name, $hash{$name}, + if ns_update($name, $hash{$name}, prereq => yxrrset("$name CNAME"), @@ -281,48 +481,161 @@ sub nscleanup { - write_cname_list($cnamelist, %hash); + write_cname_list($config{core}{cache}, %hash); +} + +### +sub com_start { + abend(EX_USAGE, "too many arguments") unless $#_ == 0; + nscleanup(); + com_reload(@_); +} + +sub com_reload { + abend(EX_USAGE, "too many arguments") unless $#_ == 0; + my $confdir = -d "$config{core}{'apache-config-directory'}/sites-enabled" + ? "$config{core}{'apache-config-directory'}/sites-enabled" + : $config{core}{'apache-config-directory'}; + my %cnames = get_cnames($confdir); + update_cnames_from_hash(%cnames); + print STDERR "$progname: no cnames defined\n" unless (keys(%cnames) > 0); } +sub com_stop { + abend(EX_USAGE, "too many arguments") unless $#_ == 0; + nscleanup; +} + +sub com_status { + err("status command ignored"); + my %stat; + + my %hash = read_cname_list($config{core}{cache}); + while (my ($name, $zone) = each %hash) { +# $name =~ s/.$zone$//; + push @{${stat}{$zone}}, $name; + } + + foreach my $zone (sort(keys %stat)) { + print "Names in zone $zone:\n"; + foreach my $name (sort(@{$stat{$zone}})) { + print " $name\n"; + } + } +} ### -($script = $0) =~ s/.*\///; +($progname = $0) =~ s/.*\///; + +my %comtab = ( + start => \&com_start, + restart => \&com_reload, + 'force-restart' => \&com_start, + reload => \&com_reload, + stop => \&com_stop, + status => \&com_status +); + +sub getcom { + my $com = shift; + + while (defined($comtab{$com}) and ref($comtab{$com}) ne 'CODE') { + $com = $comtab{$com}; + } + die "internal error: unresolved command alias" unless defined $com; + return $comtab{$com} if defined $comtab{$com}; + + my @v = map { /^$com/ ? $_ : () } sort keys %comtab; + if ($#v == -1) { + abend(EX_USAGE, "unrecognized command"); + } elsif ($#v > 0) { + abend(EX_USAGE, "ambiguous command: ".join(', ', @v)); + } + return getcom($v[0]); +} + ## Read configuration -read_config_file($ENV{'VHOSTCNAME_CONF'} ? - $ENV{'VHOSTCNAME_CONF'} : $config_file); +sub parse_ns_key { + my ($var, $ref, $loc) = @_; + my @result; + if ($$ref =~ /(.+?)=(.+)/) { + push @result, $1, $2; + $$ref = \@result; + } else { + err("$loc: $var argument must be must be NAME=KEY"); + return 0; + } + return 1; +} -GetOptions("help" => \$man, - "h" => \$help, - "debug|d+" => \$debug, - "dry-run|n" => \$dry_run, - "hostname|H=s" => \$host, - "apache-config-pattern=s" => \$confpat, - "apache-config-directory=s" => \$confdir, - "ns-key-file=s" => sub { - abend(3, "NS key already set") if ($#tsig_args >= 0); - push @tsig_args, $_[1]; - }, - "ns-key=s" => sub { - abend(3, "NS key already set") if ($#tsig_args >= 0); - if ($_[1] =~ /(.+?)=(.+)/) { - push @tsig_args, $1; - push @tsig_args, $2; +sub parse_boolean { + my ($var, $ref, $loc) = @_; + my %bool = ( yes => 1, + no => 0, + true => 1, + false => 0, + t => 1, + nil => 0, + f => 0, + on => 1, + off => 0, + 1 => 1, + 0 => 0); + + my $s = $$ref; + $s =~ tr/A-Z/a-z/; + if (exists($bool{$s})) { + $$ref = $bool{$s}; } else { - abend(3, "argument to --ns-key must be NAME=KEY"); + err("$loc: argument must be boolean"); + return 0; + } + return 1; } + +my %kw = ( + core => { + section => { + 'apache-config-directory' => 1, + 'apache-config-pattern' => 1, + 'cache' => 1, + 'server' => 1, + 'ttl' => 1, + 'ns-key' => { parser => \&parse_ns_key }, + 'ns-key-file' => 1, + 'hostname' => 1, + 'allow-wildcards' => { parser => \&parse_boolean } + }, }, - "cname-file=s" => \$cnamelist, - "zone|z=s@" => \@zone, - "ttl=i" => \$ttl, - "server=s" => \$nameserver, - "allow-wildcard-domains" => \$allow_wildcard_domains - ) or exit(3); - -pod2usage(-message => "$script: update DNS from Apache virtual host configuration", - -exitstatus => 0) if $help; -pod2usage(-exitstatus => 0, -verbose => 2) if $man; - -unless (defined($confdir)) { + zone => { + section => { + 'server' => 1, + 'ttl' => 1, + 'ns-key' => { parser => \&parse_ns_key }, + 'ns-key-file' => 1 + }, + } +); + +GetOptions("help" => sub { + pod2usage(-exitstatus => EX_OK, -verbose => 2); + }, + "h" => sub { + pod2usage(-message => "$progname: $progdescr", + -exitstatus => EX_OK); + }, + "usage" => sub { + pod2usage(-exitstatus => EX_OK, -verbose => 0); + }, + + "debug|d+" => \$debug, + "dry-run|n" => \$dry_run, + "config|c=s" => \$config_file, + ) or exit(EX_USAGE); + +readconfig($config_file, \%config, kw => \%kw); + +unless (defined($config{core}{'apache-config-directory'})) { foreach my $dir ("/etc/apache2", "/etc/httpd") { if (-e "$dir/sites-enabled" and -e "$dir/sites-available") { - $confdir = $dir; + $config{core}{'apache-config-directory'} = $dir; last; @@ -330,3 +643,3 @@ unless (defined($confdir)) { if (-e "$dir/vhosts.d") { - $confdir = "$dir/vhosts.d"; + $config{core}{'apache-config-directory'} = "$dir/vhosts.d"; last; @@ -334,9 +647,10 @@ unless (defined($confdir)) { } - abend(3, - "don't know where virtual host configurations are located; use --apache-config-directory option") - unless defined($confdir); + abend(EX_CONFIG, + "don't know where virtual host configurations are located; define apache-config-directory") + unless defined($config{core}{'apache-config-directory'}); } -$host = hostname() unless defined($host); -push(@zone, $host) if ($#zone == -1); +$config{core}{hostname} = hostname() unless defined($config{core}{hostname}); +$config{zone}{$host} = {} unless exists $config{zone}; + $debug++ if ($dry_run); @@ -344,4 +658,4 @@ $debug++ if ($dry_run); if ($#ARGV == -1) { - abend(3, "command not given") unless ($ENV{'DIREVENT_FILE'}); - print STDERR "$script: started as direvent handler for " . + abend(EX_USAGE, "command not given") unless ($ENV{'DIREVENT_FILE'}); + print STDERR "$progname: started as direvent handler for " . "$ENV{'DIREVENT_GENEV_NAME'} on $ENV{'DIREVENT_FILE'}\n" @@ -350,5 +664,6 @@ if ($#ARGV == -1) { my $update_dir; + my $confdir = $config{core}{'apache-config-directory'}; if (-d "$confdir/sites-available" && -d "$confdir/sites-enabled") { if ($cwd eq "$confdir/sites-available") { - foreach my $file (glob "$confdir/sites-enabled/$confpat") { + foreach my $file (glob "$confdir/sites-enabled/$config{core}{'apache-config-pattern'}") { next unless (-l $file); @@ -368,18 +683,7 @@ if ($#ARGV == -1) { update_cnames_from_dir($update_dir) if defined($update_dir); -} elsif ($#ARGV != 0) { - abend(3, "too many arguments"); -} elsif ($ARGV[0] =~ /^start|restart|force-restart|reload$/) { - nscleanup if ($ARGV[0] =~ /start$/); - my %cnames = get_cnames(-d "$confdir/sites-enabled" ? - "$confdir/sites-enabled" : $confdir); - update_cnames_from_hash(%cnames); - print STDERR "$script: no cnames defined\n" unless (keys(%cnames) > 0); -} elsif ($ARGV[0] eq "stop") { - nscleanup; -} elsif ($ARGV[0] eq "status") { - err("status command ignored"); -} else { - abend(3, "invalid command, try $script --help for more info"); } +my $command = getcom($ARGV[0]); +&{$command}(@ARGV); + exit($status); @@ -433,2 +737,8 @@ corresponding B<direvent.conf>(5) entry: +Unless the program is started as a B<direvent>(8) handler, exactly one +command must be given in the command line. A command may be supplied +in full or abbreviated form. Any unambiguous abbreviation is allowed. + +Available commands are: + =over 4 @@ -437,4 +747,4 @@ corresponding B<direvent.conf>(5) entry: -Scan the apache configuration files and register all server names matching -the supplied zones. +Scan the apache configuration files and register all server names that +match the configured zones. @@ -444,5 +754,14 @@ Deregister all hostnames registered previously. -=item B<restart>, B<force-restart>, B<reload> +=item B<restart>, B<reload> -Same as running B<vhostcname stop; vhostcname start>. +Builds a list of names from the apache configuration (I<apache-list>) and +compares them with the names registered at the previous run (I<cache>). If +the two lists differ, the names present in I<apache-list>, but absent in +I<cache> are registered. The names present in I<cache>, but lacking in +I<apache-list> are deleted from the DNS. + +=item B<force-restart> + +Deregister all hostnames registered previously, rescan Apache files, and +register all names that match the configured zones. @@ -450,3 +769,3 @@ Same as running B<vhostcname stop; vhostcname start>. -Ignored +Displays registered host names. @@ -458,9 +777,52 @@ Ignored -=item B<--allow-wildcard-domains> +=item B<-c>, B<--config=>I<FILE> + +Read configuration from I<FILE> instead of the default location +(F</etc/vhostcname.conf>). + +=item B<-d>, B<--debug> + +Increases the debug level. Multiple B<-d> options are allowed. + +=item B<-n>, B<--dry-run>, + +Enables I<dry-run> mode: print what would have been done without actually +doing it. + +=item B<--help> + +Displays B<vhostcname> man page. + +=item B<-h> + +Displays a short help summary and exits. + +=item B<--usage> + +Displays a short command line syntax reminder. + +=back + +=head1 CONFIGURATION FILE + +Configuration is read from F</etc/vhostcname.conf> or a file specified +by the B<--config> (B<-c>) command line option. The file consists of +a number of variable assignments (I<variable> B<=> I<value>), grouped into +sections. Whitespace is ignored, except that it serves to separate input +tokens. I<value> is read verbatim, including eventual whitespace characters +that can appear within it. + +A section begins with the line containing its name within square brackets +(e.g. B<[core]>). The name can be followed by one or more arguments, if +the section semantics requires so (e.g. B<[zone example.com]>). + +The following sections are recognized: + +=over 4 + +=item B<[core]> -Allow the use of wildcard (B<*>). When this option is in effect, a wildcard -will be allowed if it is the very first label in a domain name and it is -separated from the base zone (see the B<--zone> option) by one or more labels. +=over 8 -=item B<--apache-config-directory=>I<DIR> +=item B<apache-config-directory => I<DIR> @@ -476,3 +838,3 @@ terminate if unable to do that. -=item B<--apache-config-pattern=>I<GLOB> +=item B<apache-config-pattern => I<PATTERN> @@ -480,36 +842,47 @@ Shell globbing pattern for virtual host configuration files. By default, B<*> is used, meaning that B<vhostcname> will scan all files in the -configuration directory. +configuration directory (note: that includes backup copies too!). -=item B<--cname-file=>I<NAME> +=item B<cache => I<FILE> -Name of the file where B<vhostcname> will keep successfully registered +Name of the cache file where B<vhostcname> keeps successfully registered host names. Default is B</var/run/vhostcname.cache>. -=item B<-d>, B<--debug> +=item B<hostname => I<HOSTNAME> -Increases the debug level. Multiple B<-d> options are allowed. +Sets the hostname. Use this if B<vhostcname> is unable to correctly +determine it. -=item B<-n>, B<--dry-run>, +=item B<allow-wildcards => I<BOOL> -Enables I<dry-run> mode: print what would have been done without actually -doing it. +Allow the use of wildcard (B<*>) in host names. When this option is in +effect, a wildcard will be allowed if it is the very first label in a domain +name and it is separated from the base zone (see the B<zone> section) by one +more labels. -=item B<--help> +I<BOOL> is one of B<yes>, B<true>, B<t>, B<on>, or B<1> to allow wildcards, +or one of B<no>, B<false>, B<f>, B<nil>, B<off>, B<0> to disallow them (the +default). -Displays B<vhostcname> man page. +=back -=item B<-h> +The following variables provide defaults for zones that lack the +corresponding settings: -Displays a short help summary and exits. +=over 8 -=item B<-H>, B<--hostname>=I<NAME> +=item B<server => I<HOST> -Sets the hostname. Use this if B<vhostcname> is unable to correctly -determine it. +Name of the DNS server to use. Normally B<vhostcname> determines what server +to use based on the B<SOA> record of the zone to be updated, so this option +is rarely needed. -=item B<--ns-key=>I<NAME>=I<KEY> +=item B<ttl => I<SECONDS> -Define the TSIG key. +TTL value for new DNS records. Default is 3600. -=item B<--ns-key-file=>I<KEYFILE> +=item B<ns-key => I<NAME>=I<HASH> + +Defines the TSIG key. + +=item B<ns-key-file => I<FILE> @@ -519,46 +892,41 @@ file can be used. -This option cannot be used together with B<--ns-key>. +If both <ns-key> and B<ns-key-file> are used, the latter is given preference. -=item B<--server=>I<NAME> +=back -Name of the DNS server to use. Normally B<vhostcname> determines what server -to use based on the B<SOA> record of the zone to be updated, so this option -is rarely needed. +=item B<[zone I<NAME>]> -=item B<--ttl=>I<TIME> +The B<zone> section informs B<vhostcname> that it should handle names +in zone I<NAME>. Any number of B<[zone]> sections can be defined. If +none is defined, B<vhostcname> will take hostname as the name of the zone +to update. -TTL value for new DNS records. Default is 3600. +The variables in a B<zone> section define parameters to be used for that +particular zone. As such, none of them is mandatory. If the zone I<NAME> +uses default settings (or settings, defined in the B<[core]> section), +the section can be empty. -=item B<--zone=>I<NAME> +=over 8 -Name of the zone which B<vhostcname> can update. Multiple B<--zone> options -can be given. +=item B<server => I<HOST> -If no B<--zone> option is given, B<vhostcname> will take hostname as the -name of the zone. +Name of the DNS server to use when updating this zone. -=back +=item B<ttl => I<SECONDS> -=head1 CONFIGURATION FILE +TTL for records in this zone. -If the file B<etc/vhostcname.conf> exists, the program will read its -configuration from it. A familiar UNIX configuration format is used. -Empty lines and UNIX comments are ignored. Each non-empty line is either an -option name, or option assignment, i.e. B<opt>=B<val>, with any amount of -optional whitespace around the equals sign. Valid option names are -the same as the long command line options, but without the leading B<-->. -For example: +=item B<ns-key => I<NAME>=I<HASH> - zone = vhost.example.com - ns-key-file = /etc/bind/Kvhost+157+43558.key - ttl = 3600 +TSIG key. -=head1 ENVIRONMENT +=item B<ns-key-file => I<FILE> -=over 4 +Name of the key file. The argument should be the name of a file +generated by the B<dnssec-keygen> utility. Either B<.key> or B<.private> +file can be used. -=item B<VHOSTCNAME_CONF> +If both <ns-key> and B<ns-key-file> are used, the latter is given preference. -The name of the configuration file to use instead of the default -F</etc/vhostcname.conf>. +=back @@ -576,19 +944,21 @@ Success -Operating system error (unable to open file, etc.) +Some of the host names could not be updated. -=item 2 +=item 64 -Some of the host names could not be registered. +Command line usage error. -=item 3 +=item 66 -Command line usage error +Required input file cannot be opened. -=back +=item 73 -=head1 BUGS +Required output file cannot be created or written. -Only one key file can be given. This means that if you use multiple -B<--zone> options, all zones must be configured to accept the same -DNSSEC key. Ditto for the B<--server> option. +=item 78 + +Configuration error. + +=back |