#!/usr/bin/perl -w #============================================================================== # lat-hosts # ========= # 0.9.0 (2004-09-08) # (c)2003-2004 Altiplano bvba #============================================================================== package esmith; use strict; use esmith::config; use esmith::db; use esmith::util; use Getopt::Long; use Pod::Usage; my %conf; tie %conf, 'esmith::config'; my %accounts; tie %accounts, 'esmith::config', '/home/e-smith/db/accounts'; my %hosts; tie %hosts, 'esmith::config', '/home/e-smith/db/hosts'; my %domains; tie %domains, 'esmith::config', '/home/e-smith/db/domains'; my ($Hlp, $Cml, $Frc, $Inp); my $Add =0; my $Del =0; #============================================================================== # Main #============================================================================== sub performCreateLocalHostEntry ($$$$$$$); # Analyze commandline options GetOptions ("help" => \$Hlp, "add" => \$Add, "delete" => \$Del, "force" => \$Frc, "command-line=s" => \$Cml, "input-file=s" => \$Inp); if ( $Hlp ) { &PrintPod(9); exit; } # We need one argument or the other, but not both if ((($Cml && $Inp) || (! $Cml && ! $Inp)) || ($Add + $Del != 1)) { &PrintPod(1); exit; } my @records; if ($Inp) { open(LIST,"< $Inp") || die "Can't find $Inp.\n"; @records = grep(!/(^\s*#)|(^\s*$)/,); close(LIST); } elsif ($Cml) { @records=($Cml); } else { &PrintPod(1); exit; } if ($Add) { # Process each user foreach my $record (@records) { my @fields=split(/\|/,$record); for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }} performCreateLocalHostEntry ($fields[0], # HostName $fields[1], # DomainName $fields[2], # Location $fields[3], # Visibility $fields[4], # Local IP $fields[5], # Global IP $fields[6]); # Mac Address } } if ($Del) { &ExpandWildCard; # Check for wildcards and expand if necessary # Process each user foreach my $record (@records) { my @fields=split(/\|/,$record); for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }} if (! $fields[1] ) { $fields[1] = db_get(\%conf, 'DomainName') } my $FullHostName = lc($fields[0]).".".lc($fields[1]); if ((db_get(\%hosts, $FullHostName)) && (db_get_type(\%hosts, $FullHostName)) eq "host") { my $yn = 'yes'; if (! $Frc) { print "Do you want to delete host '$FullHostName' [yes/NO/all]? "; $yn = ; if ($yn =~ /^a/i) { $Frc = -1; $yn="yes"; } } if ($yn =~ /^y/i) { print "Deleting '$FullHostName'...\n"; db_delete(\%hosts, $FullHostName); system ("/sbin/e-smith/signal-event", "host-delete", "$FullHostName") == 0 or die ("Error occurred while deleting '$FullHostName'.\n"); } } else { print "Can't find host '$FullHostName'.\n\a";} } } #============================================================================== # Subroutines #============================================================================== # Create local host sub performCreateLocalHostEntry ($$$$$$$) { my $REGEXPMACAddress = '([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f]){5})'; my $REGEXPIPAddress = '(self|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'; my $REGEXPHostname = '([a-z0-9][a-z0-9-]*)'; my ($q) = @_; my ($HostName, $DomainName, $Location, $Visibility, $InternalIP, $ExternalIP, $MACAddress) = @_; my %hostProperties; #------------------------------------------------------------ # Validate parameters and untaint them #------------------------------------------------------------ # Hostname $HostName = lc($HostName); if ($HostName =~ /^$REGEXPHostname$/) { $HostName = $1; } else { print "Bad hostname ($HostName).\a\n"; return; } if (length $HostName > 32) { print "Hostname too long.\a\n"; return; } # Domainname if (! $DomainName) { $DomainName = db_get(\%conf, 'DomainName') } $DomainName = lc($DomainName); if ((! db_get(\%domains, $DomainName)) && ( db_get(\%conf, 'DomainName') ne $DomainName)) { print "\nThe domainname '$DomainName' is not hosted on this server.\a\n"; return; } if ($DomainName =~ /^([a-z0-9\-\.]+)$/) { $DomainName = $1; } else { print "Unexpected characters in '$DomainName'.\a\n"; return; } print "\nCreating $HostName.$DomainName...\n"; # Location if (! $Location) { $Location = "Self" } if ( $Location =~ /(local)|(remote)|(self)/i ) { $Location = ucfirst lc $Location; } else { $Location = "Self" } # Visibility if ( ! $Visibility ) { $Visibility = "Local"; $ExternalIP = ""; } if ( $Visibility =~ /(local)|(global)/i ) { $Visibility = ucfirst lc $Visibility; } else { $Visibility = "Local" } if ($Visibility eq "Local") { $ExternalIP = "" } # Internal IP address if (! $InternalIP) { $InternalIP = ''; } if ($Location eq 'Self') { $InternalIP = '' } elsif ($InternalIP =~ /^$REGEXPIPAddress$/ ) { $InternalIP = $1; } else { print "Error: IP Address '$InternalIP' is invalid. Did not create hostname.\a\n"; return; } # MAC address if (( ! $MACAddress ) || ($Location ne "Local")) { $MACAddress = "" } if ( length($MACAddress) == 0 ) { # They don't want one } elsif ($MACAddress =~ /^$REGEXPMACAddress$/i ) { $MACAddress = $1; } else { print "Invalid Mac Address \n"; return; } # External IP if ( ! $ExternalIP ) { $ExternalIP = "" } if ($Visibility eq 'Global') # Wants externally visible - needs external IP { if ( length ($ExternalIP) == 0 ) { print "Error: You did not enter a Global IP value for '$HostName'.\a\n"; return; } elsif ($ExternalIP =~ /^$REGEXPIPAddress$/ ) { $ExternalIP = $1; } else { print "Error: IP Address '$ExternalIP' is invalid. Did not create hostname.\a\n"; return; } } else # Wants internally visible - optional external IP { if ( length ($ExternalIP) == 0 ) { # They didn't specify one } elsif ($ExternalIP =~ /^$REGEXPIPAddress$/ ) { $ExternalIP = $1; } else { print "Error: IP Address '$ExternalIP' is invalid. Did not create hostname.\a\n"; return; } } #------------------------------------------------------------ # Looks good. Find out if this entry has been taken #------------------------------------------------------------ my $fullHostName = join (".", $HostName, $DomainName); if (defined (db_get(\%hosts, $fullHostName)) ) { print "HostName '$fullHostName' already exists. \n"; return; } #------------------------------------------------------------ # Host entry is available! Update hosts database. #------------------------------------------------------------ print "HostName : $HostName\n"; print "DomainName : $DomainName\n"; print "Location : $Location\n"; print "Visibility : $Visibility\n"; print "Local IP : $InternalIP\n"; print "Global IP : $ExternalIP\n"; print "Mac Address : $MACAddress\n"; $hostProperties{'Visibility'} = $Visibility; $hostProperties{'HostType'} = $Location; $hostProperties{'InternalIP'} = $InternalIP; $hostProperties{'ExternalIP'} = $ExternalIP; $hostProperties{'MACAddress'} = $MACAddress; db_set(\%hosts, $fullHostName, 'host', \%hostProperties); #------------------------------------------------------------ # Signal the create-host event. #------------------------------------------------------------ system ("/sbin/e-smith/signal-event", "host-create", "$HostName") == 0 or die ("Error occurred while creating hostname.\n"); } #============================================================================== # Test for wildcards in the hostname. If any wildecards are found, the array # @records is expanded with the hostnames that meet the conditions. sub ExpandWildCard { my $ctrec = 0; foreach my $record (@records) { my @fld=split(/\|/,$record); for (my $cnt=0; $cnt <= $#fld; ++$cnt) { for ($fld[$cnt]) { s/^\s+//; s/\s+$//; }} if ($fld[0] =~ /\*|\?/) { # Does it contain the wildcards? $fld[0] =~ s/\*/\.\*/g; # Replace * with .* to allow for grep. $fld[0] =~ s/\?/\./g; # Replace ? with . to allow for grep. if (! $fld[1]) { $fld[1] = db_get(\%conf, 'DomainName'); } open USRS, "; close(USRS); my $cu = 0; foreach my $tst (@match) { $tst =~ /\.$fld[1]\=/; $tst = $`; for (my $cnt=1; $cnt <= $#fld; ++$cnt) { $tst = $tst." | ".$fld[$cnt]; }; if ($cu == 0 ) { $records[$ctrec] = $tst; $cu =1; } else { push(@records, $tst); } } } ++$ctrec; } } #============================================================================== # Print the pod text as a help screen sub PrintPod { my ($verbose, $message) = @_; pod2usage(-verbose => $verbose, -message => $message, -exitval => 64); } #============================================================================== =pod =head1 NAME B - The lazy administrator's tool to manage hostnames =head1 DESCRIPTION Creates or deletes hosnames on Mitel's SME servers. This tool is functionally equivalent to the 'Hostnames and addresses' option in the server-manager, but can be run from the command line or called from an other script. It allows you, for example, to create a large number of hostnames in a batch process, or delete hostnames on a remote machine via an ssh console. See F for the format of the input file. =head1 SYNOPSIS B -a -c "host| domain| loc. | visib. | loc.ip | glob.ip | mac" B -a -i /path/to/hosts.list B -d [-f] -c "Host | Domain" B -d [-f] -i /path/to/hosts.list =head1 OPTIONS The following options are supported: =over 4 =item B<-a>, B<--add> Add a host name to the server =item B<-c "Arguments">, B<--command-line="Arguments"> Take arguments from the command line. See below for the various arguments that are accepted. =item B<-d>, B<--delete> Delete a hostname from the server. Wildcards (* and ?) are accepted. =item B<-f>, B<--force> Don't prompt before deleting. =item B<-h>, B<--help> Extended help for this tool =item B<-i FILE>, B<--input-file=FILE> Use the information from F to create or delete the hostname(s). =back =head2 Arguments: host* : Must contain only letters, numbers, and hyphens, and must start with a letter or number. Wildcards (* and ?) can only be used to delete hostnames. domain : Must be an existing (virtual) domain name. If omitted, the primary site is assumed. loc. : Location of the server: 'Local' (on the LAN) 'Remote' (on the Internet) 'Self' (alias for the SME server) visib. : 'Local' (only visible on the LAN) 'Global' (visible on the entire Internet). loc.ip : Internal IP number glob.ip : Public IP number mac : The ethernet address is optional and causes the DHCP server to statically bind the local IP address to the computer with this ethernet address. * mandatory field =head1 EXAMPLES B Creates host name 'ntp.hogwarts.net' on the SME server. The host name will be visible only on the LAN. B Creates the host names defined in F. Refer to F for an example of an input file. B Deletes all host names that start with 'ftp'. The host names will be deleted without prompting (-f). =head1 SEE ALSO lat-group(8), lat-pseudonyms(8), lat-ibays(8), lat-quota(8), lat-domains(8), lat-users(8), lat-procmail(8), lat-pptp(8), lat-dump(8) =head1 VERSION Version 0.9.0 (2004-09-08). The latest version is hosted at B =head1 COPYRIGHT (c)2003-2004, Altiplano bvba (B). Released under the terms of the GNU license. =head1 BUGS Please report bugs to =cut #==============================================================================