#!/usr/bin/perl -w
#
# "SystemImager"
# 
#   Copyright (C) 1999-2005 Brian Elliott Finley
#   Copyright (C) 2002 Internation Business Machines
#                      Sean Dague <sean@dague.net>
#
#   $Id: si_prepareclient 3025 2005-03-07 04:13:39Z brianfinley $
# 
#   2004.02.02 Brian Elliott Finley
#   - add '--server' option
#   2004.06.01 Brian Elliott Finley
#   - set rsyncd.conf's host_allow to _not_ include localhost
#   2004.10.12  Brian Elliott Finley
#   - prefer parted over sfdisk, unless i386.  was failing on ppc.
#   2005.01.12  Brian Elliott Finley
#   - add --exclude DISK option
#   2005.01.29  Andrea Righi <a.righi@cineca.it>
#   - add lvm information saving
#
#
#   Function: si_prepareclient is used to, well, prepare a client to have 
#   it's image retrieved by an imageserver
# 
#   This is a port to Perl of the original bash script written by 
#   Brian Elliott Finley.  Some of the bash code was contributed by 
#   Jose AP Celestino <japc@sl.pt>.
# 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
#   Brian's thoughts:
#
#   My wedding ring has Hebrew writing around it's circumference.  When people
#   ask me what it says, I usually say "Live to ride.  Ride to live."  But it
#   actually says: "I am my beloved's and she is mine" which is adapted from the
#   Song of Solomon.  http://www.bible.org/netbible/sos7.htm
#
#   The Beloved:
#
#     7:10 I am my beloved's,
#     and he desires me!
#    
#
#   The Beloved to Solomon:
#    
#     7:11 Come, my beloved, let us go to the countryside;
#     let us spend the night in the villages.
#    
#     7:12 Let us rise early to go to the vineyards,
#     to see if the vines have budded,
#     to see if their blossoms have opened,
#     if the pomegranates are in bloom?
#     there I will give you my love.
#    
#     7:13 The mandrakes send out their fragrance;
#     over our door is every delicacy,
#     both new and old, which I have stored up for you, my lover. 
#

use lib "USR_PREFIX/lib/systemimager/perl";
use strict;
use Carp;
use POSIX;
use File::Copy;
use File::Path;
use File::Basename;
use Getopt::Long;
use vars qw($VERSION);
use SystemImager::Common;
use SystemImager::UseYourOwnKernel;

# set version
$VERSION = "SYSTEMIMAGER_VERSION_STRING";

# set extension to use when backing up config files
my $backup_extension = ".before_systemimager-$VERSION";

# configuration directory
my $systemimagerdir = "/etc/systemimager";

# location of temporary rsyncd.conf file
my $rsyncd_conf_file = "/tmp/rsyncd.conf.$$";

# set path
$ENV{PATH} = "/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin";

# we rely on english text when parsing command output
$ENV{"LANG"} = "C";

my $progname = basename $0;

my $version_info = <<"EOF";
$progname (part of SystemImager) v$VERSION

Copyright (C) 1999-2005 Brian Elliott Finley

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF

my $help_info = $version_info . <<"EOF";

Usage: $progname --server IMAGESERVER [OPTION]...

Options:
 --version             
    Display version and copyright information.

 --help                
    Display this output.

 --server IMAGESERVER
    Where IMAGESERVER is either the IP address or host name of your 
    SystemImager server.  Prepareclient makes your golden client's root
    filesystem readable to other rsync capable machines.  While this
    access is read only, it may expose sensitive information.  This
    option limits access so that only your SystemImager server can read
    your golden client's root filesystem.

 --no-rsyncd           
    Do not start the rsync daemon.

 --yes                 
    Answer yes to all yes/no questions.

 --exclude, -e DISK [-e DISK...]
    Do not gather partition information for DISK(s).  The result of 
    using this option, is that DISK(s) will not be partitioned during
    the auto-install process.

    You may prefix DISK with "/dev/", but it is not necessary, and a
    base directory of /dev/ will be assumed.  

    Example: -e /dev/sdb -e sdc -e /dev/ida/c0d0 -e ida/c0d1

 --quiet
    Run silently.  Return an exit status of 0 for success or a non-zero
    exit status for failure.

 --rpm-install         
    This is only used when building an RPM.

Download, report bugs, and make suggestions at:
http://systemimager.org/

EOF


GetOptions(
    "explicit"             => \my $explicit,
    "help"                 => \my $help,
    "exclude-disk|e=s"     => \my @exclude_disk,
    "no-rsyncd|norsyncd"   => \my $norsyncd,
    "quiet"                => \my $quiet,
    "rpm"                  => \my $rpm,
    "version"              => \my $version,
    "server=s"             => \my $server,
    "yes"                  => \my $yes
) || die "$help_info";


### BEGIN option validation ###
# show version if requested
if($version) {
    print $version_info;
    exit 0;
}

# give help if requested
if($help) {
    print "$help_info";
    exit 0;
}

# bail if not root
if ($> != 0) {
    print "Must be run as root!\n";
    exit 1;
}

if($explicit) {
    # Depricated.  Remove after v3.2.x.  Today's date: 2003.06.21 -BEF-  
    print "FATAL:  -explicit is now depricated.  If SystemImager does not work properly\n";
    print "        for you without the -explicit option, please file a bug report at\n";
    print "        http://systemimager.org/support/.  Thanks!\n";
    print "\n";
    print qq(Try "$progname --help" for more options.\n);
    exit 1;
}

# Make sure we have certain tools that we need.
which('rsync') or croak("'rsync' is required for $progname to function properly.  Please see http://systemimager.org for more details.");
which('systemconfigurator') or croak("'systemconfigurator' is required for $progname to function properly.  Please see http://systemimager.org for more details.");

# -rpm-install does the same thing as -no-rsyncd.  -BEF-
if($rpm) {
    $norsyncd = "1";  # set it to true.
}

my $hosts_allow;
if($server) {
        if($server =~ m/^(\d{1,3}\.){3}\d{1,3}$/) {
                $hosts_allow .= "${server}/32";
        } else {
                my @ips = get_ips($server);
                foreach (@ips) {
                        $hosts_allow .= "${_}/32 ";
                }
        }
} else {
        print "$help_info";
        print "Try the --server option.\n\n";
        exit 1;
}
### END option validation ###


unless(($quiet) or ($yes)) {
    # do the interactive part
    system("clear");
    print <<EOF;
Welcome to the SystemImager $progname command.  This command may modify
the following files to prepare your golden client for having it's image 
retrieved by the imageserver.  It will also create the /etc/systemimager 
directory and fill it with information about your golden client.  All modified
files will be backed up with the $backup_extension extension.

 /etc/services:
   This file defines the port numbers used by certain software on your system.
   Entries for rsync will be added if necessary.

 $rsyncd_conf_file:
   This is a temporary configuration file that rsync needs on your golden client
   in order to make your filesystem available to your SystemImager server.

 inetd configuration:
   SystemImager needs to run rsync as a standalone daemon on your golden client
   until it's image is retrieved by your SystemImager server.  If rsyncd is 
   configured to run as a service started by inetd, it will be temporarily
   disabled, and any running rsync daemons or commands will be stopped.  Then,
   an rsync daemon will be started using the temporary configuration file
   mentioned above.
   
See "$progname --help" for command line options.

EOF

    print "Continue? (y/[n]): ";
    my $answer = <>;
    unless($answer =~ /y/i) {
        print "Client prepartion cancelled.  No files modified.\n";
        exit 1;
    }
    
    system("clear");
    print <<EOF;
*********************************** WARNING *********************************** 
This utility starts an rsync daemon that makes all of your files accessible
by anyone who can connect to the rsync port of this machine.  This is the 
case until you reboot, or kill the 'rsync --daemon' process by hand.  By 
default, once you use si_getimage to retrieve this image on your imageserver, 
these contents will become accessible to anyone who can connect to the rsync 
port on your imageserver.  See rsyncd.conf(5) for details on restricting 
access to these files on the imageserver.  See the systemimager-ssh package
for a more secure method of making images available to clients.
*********************************** WARNING *********************************** 

EOF

  
  # you sure you want to install?
    print "Continue? (y/[n]): ";
    $answer = <>;
    unless($answer =~ /y/i) {
        print "Client prepartion cancelled.  No files modified.\n";
        exit 1;
    }

    #
    # Deal with supermount module
    #
    system("clear");
    my @supermounts;
    open(MOUNT, "mount |");
        while(<MOUNT>) {
            my @fields = split;
            if("$fields[4]" eq "supermount") {
                push @supermounts, $fields[2];
            }
        }
    close(MOUNT);
    
    if (@supermounts) {
        print << "EOF";
You appear to have mount points managed by the supermount kernel module.  These
supermount managed mount points will cause your image retrieval to fail, unless
they are unmounted.  

Devices managed by supermount are typically CDROM and floppy devices, which
won't be retrieved by SystemImager anyway, so unmounting these devices should
cause no harm.  You can safely re-mount them after running the si_getimage 
command from your SystemImager server.

Here are the mount points on this system that are managed by supermount:
EOF

        print "\n";
        foreach (@supermounts) {
            print "    $_\n";
        }
        print "\n";
        print "SystemImager needs to unmount these to continue.\n\n";

        print "Continue? (y/[n]): ";
        my $answer = <>;
        unless($answer =~ /y/i) {
            print "Client prepartion cancelled.  No files modified.\n";
            exit 1;
        }
        foreach (@supermounts) {
            my $cmd = "umount $_";
            !system($cmd) or die("Couldn't $cmd.");
        }
    }
 
    my $result;
    $result = `ps axww|grep -v grep|grep -w rsync`;
    if (!$result) {
        $result = `ps axww|grep -v grep|grep -w rsyncd`;
    }
    if ($result) {
        print "One or more rsync daemons appear to be running on this machine.  If you\n";
        print "continue, those daemons will be killed.\n";
        print "\n";
        print "Continue? (y/[n]): ";
        my $answer = <>;
        unless($answer =~ /y/i) {
            print "Client prepartion cancelled.  No files modified.\n";
            exit 1;
        }
    }
}

# verify that rsync entry is in /etc/services
add_rsync_services();

# process user specified list of disks to exclude
my %excluded_disks;
foreach(@exclude_disk) {
	
	# Strip off the /dev/ part.  It's just for user non-confusion anyway. -BEF-
	$_ =~ s#^/dev/##;
	$_ =~ s#^/##;	# lose a leading / if someone happened to use one (Ie. /sda).

	#
	# Put entries without /dev/ prefixes into hash, and set the values to match
	# the keys so that we can do a simple "if defined" test later. 
	# -BEF- 2005.01.12
	#
	$excluded_disks{$_} = $_;
}

# comment out rsync entry in inetd.conf if it exists
temporarily_disable_rsync_in_inetd();

# get rid of xinetd configuration for rsync if it exits
temporarily_disable_rsync_in_xinetd();

# Collect all the disk information
my $disks = collect_disks();

# Create /etc/systemimager/autoinstallscript.conf
create_auto_install_script_conf($disks);

# Run the rsync daemon for getimage?
unless($norsyncd) {
    # install SystemImager brand rsyncd.conf file ($rsyncd_conf_file)
    create_rsyncd_conf($rsyncd_conf_file);
    
    # rsync < v2.5.x requires that a host have an entry with it's hostname
    # in it's /etc/hosts before rsync will start up in daemon mode.  I've 
    # always wanted a better way of doing this than simply adding an entry
    # and leaving it there.  It's usually harmless, but I prefer to not leave
    # footprints.  I've determined that if you add an entry, start up rsync
    # in daemon mode, then remove the entry, things work fine.  So now we do 
    # just that. -BEF-
    my $hostname = (uname)[1];
    my $file = "/etc/hosts";
    
    # kill off any running rsync daemons
    killall("rsync",1);
    killall("rsyncd",1);
    
    # Make a copy of the original /etc/hosts file.
    my $source      = $file;
    my $destination = "$file.before-$progname";
    copy($source, $destination) or die "FATAL: Failed to copy $source to $destination.\n";
    
    # Append our temporary entry.
    open(TMP,">>$file") or croak("Couldn't open $file for writing.");
      print TMP "127.0.0.1  $hostname\n";
    close(TMP);
    
    
    # start up our fresh daemon
    if(!$quiet) {
    
        # Give a couple of seconds for the old daemon to die.
        print "Starting or re-starting rsync as a daemon";
        $|++;
        foreach (1..2) {
             sleep 1;
             print ".";
        }
    
        # Start up the new one.
        system("rsync --daemon --config=$rsyncd_conf_file");
    
        # Give a few seconds for the new daemon to start.
        $|++;
        foreach (3..5) {
             sleep 1;
             print ".";
        }
        print "\n";
    
        # Wrap up
        print "done!\n";
    } else {
      # still need to sleep
      sleep 2;
      system("rsync --daemon --config=$rsyncd_conf_file");
      sleep 3;
    }
    
    # Put the original /etc/hosts file back (sans our temporary entry).
    $source      = "$file.before-$progname";
    $destination = $file;
    move($source, $destination) or die "FATAL: Failed to copy $source to $destination.\n";
}
### END leave disk info behind for the getimage command ###

# In the case that /etc/mtab is a symlink to /proc/mounts.  This 
# method should still work.  This file we create here is also left 
# behind for getimage. -BEF-
system("mount > /etc/systemimager/mounted_filesystems");

# wrap up
if(!$quiet and !$norsyncd) {
    print <<EOF;

This client is ready to have it's image retrieved.  You must now run 
the "si_getimage" command on your imageserver.
EOF

} elsif(!$quiet) {
    print <<EOF;

WARNING:  The rsync daemon was not started.  You must run $progname again,
          without the -n option, before you can pull this client's image
          to an imageserver.
EOF

}

exit(0);

### BEGIN functions
# SystemImager specific functions

# a pure perl version of which
sub which {
    my $prog = shift;
    foreach my $path (split(':',$ENV{PATH})) {
        if(-x "$path/$prog") {
            return 1;
        }
    }
    return 0;
}

#
# Read /proc/partitions and figure out all the disks that need to have their 
# partitions captured.
#
sub collect_disks {

    my $disks;
    my $devfsscsi = 0;

    open(IN,"</proc/partitions") or croak("Couldn't open /proc/partitions for reading.");
    while(<IN>) {

        if(/(\S*c[0-9]+d[0-9]+)p[0-9]+/) { # hardware raid devices (/dev/rd/c?d?, /dev/ida/c?d?, /dev/cciss/c?d?)
            my $disk = $1;
            unless(defined $excluded_disks{$disk}) {
                $disks->{HWRAID}->{$disk}++;
            }

        } elsif (/(\S*[hs]d[a-z])[0-9]/) { # standard disk devices
            my $disk = $1;
            unless(defined $excluded_disks{$disk}) {
                $disks->{IDESCSI}->{$disk}++;
            }

        } elsif (/\b(ide\/host\S+disc)\b/) { # devfs standard for ide disk devices
            #          vvvvvvvvvvvvvvvvvvv -> strip off the partition number
            my $disk = devfs_transform($1);

            unless(defined $excluded_disks{$disk}) {
                $disks->{IDESCSI}->{$disk}++;
            }

        } elsif (/\b(scsi\/host\S+disc)\b/) { # devfs standard for scsi disk devices

            #
            # If we have a devfs scsi disk and we want to get
            # back to old school format, we just count up each disk
            # and assign it to /dev/sdN in order.
            #
            my $disk = "sd" . chr(97 + $devfsscsi);
            $devfsscsi++;

            unless(defined $excluded_disks{$disk}) {
                $disks->{IDESCSI}->{$disk}++;
            }
        }
    }
    close(IN);
    return $disks;
}


# Usage:
# create_auto_install_script_conf($disks);
sub create_auto_install_script_conf {

    my $disks = shift;

    unless ($quiet) { 
        system("clear");
    }

    # Remove old-style partitionschemes directory if it exists. -BEF-
    rmtree("$systemimagerdir/partitionschemes");

    # Where the configuration information be stored. -BEF-
    my $file = "$systemimagerdir/autoinstallscript.conf";

    # Determine which partition tool is available.  Preference is sfdisk -- 
    # it provides more information. -BEF-
    #
    my $partition_tool = which_partition_tool();

    SystemImager::Common->write_auto_install_script_conf_header($file);

    # First we do Hardware RAID devices
    foreach my $disk (sort keys %{$disks->{HWRAID}}) {

        my $label_type = SystemImager::Common->get_disk_label_type($partition_tool, $disk);
        if (($label_type eq "gpt") and ($partition_tool eq "sfdisk")) {
            $partition_tool = which_partition_tool("parted");
        }
        if (($label_type eq "bsd") and ($partition_tool eq "sfdisk")) {
            $partition_tool = which_partition_tool("parted");
        }
        unless ($quiet) { 
            print qq(Using "$partition_tool" to gather information about disk:\n);
            print qq(    /dev/$disk\n);
            print qq(\n);
        }
        SystemImager::Common->save_partition_information($disk, $partition_tool, $file, $label_type);

    }

    # Now we do /dev/ide and /dev/sda disks
    foreach my $disk (sort keys %{$disks->{IDESCSI}}) {

        my $label_type = SystemImager::Common->get_disk_label_type($partition_tool, $disk);
        if (($label_type eq "gpt") and ($partition_tool eq "sfdisk")) {
            $partition_tool = which_partition_tool("parted");
        }
        if (($label_type eq "bsd") and ($partition_tool eq "sfdisk")) {
            $partition_tool = which_partition_tool("parted");
        }
        unless ($quiet) { 
            print qq(Using "$partition_tool" to gather information about disk:\n);
            print qq(    /dev/$disk\n);
            print qq(\n);
        }
        SystemImager::Common->save_partition_information($disk, $partition_tool, $file, $label_type);

    }

    SystemImager::Common::save_lvm_information($file);
        
    SystemImager::Common->save_filesystem_information("/etc/fstab", $file);

    ############################################################################
    #
    # Which devstyle should BOEL use?
    #
    open(FILE, ">>$file") or die("Couldn't open $file for appending.");
        print FILE qq(\n);
        print FILE qq(  <boel devstyle=);
        if(SystemImager::Common->is_devfs_client()) {
            print FILE qq("devfs");
        } else {
            print FILE qq("static");
        }
        print FILE qq(/>\n);
        print FILE qq(\n);
    close(FILE);
    #
    ############################################################################

    #
    #   Do UYOK stuff
    #
    my $arch = get_arch();
    my $verbose;
    unless($quiet) { $verbose = 1; }
    SystemImager::UseYourOwnKernel->create_uyok_initrd( $arch, $verbose );

    SystemImager::Common->write_auto_install_script_conf_footer($file);

    #
    # END partition_tool friendly output
    #
    return 1;
}

#
# Usage: my $arch = get_arch();
#
sub get_arch {
	my $arch = (uname())[4];
	$arch =~ s/i.86/i386/;
	return $arch;
}


# Usage:
# my $partition_tool = which_partition_tool();
# my $partition_tool = which_partition_tool("preferred_tool");
sub which_partition_tool {

        my $preferred_tool = shift;
        my $partition_tool;
        
        # 
        # We prefer sfdisk on x86, for everything else we prefer parted. -BEF-
        #
        my $arch = get_arch();
        if($arch eq "i386") {
                $preferred_tool = 'sfdisk';
        }
        
        # Check to see if preferred tool is available.
        if ( ($preferred_tool) and (which("$preferred_tool")) ) {
                $partition_tool = $preferred_tool;
        
        } else {
        
                # Determine which partition tool is available.  Preference is sfdisk. -BEF-
                if (which('parted')) { 
                        $partition_tool="parted";

                } elsif (which('sfdisk')) {
                        $partition_tool="sfdisk";

                } else {
                        print qq(FATAL: I can't find an appropriate partition tool.  Please install "parted"\n);
                        print qq(       or "sfdisk", depending on what's appropriate for your architecture!\n);
                        exit 1;
                }
        
        }
        
        return $partition_tool;
}


sub temporarily_disable_rsync_in_xinetd {
    # this is the trouble file in an xinted environment
    my $file = "/etc/xinetd.d/rsync";
    if(-e $file) {
	#
        # Move rsync entry out of the way -- xinetd should ignore ~ files. -BEF-
	#
        move($file,$file . '~');

	#
	# restart xinetd with rsync disabled -BEF-
	#
        print "Signaling xinetd to restart...\n" unless($quiet);
        killall('xinetd',12); # Send SIGHUP to all xinetd processes

	#
	# Put rsync entry back, so that we leave the system as we found it.
	# Won't take effect though until system is rebooted, or xinetd 
	# restarted again. -BEF-
	#
        move($file . '~',$file);
    }
    return 1;
}

sub temporarily_disable_rsync_in_inetd {
    my $file = "/etc/inetd.conf";
    my $rsyncfound = 0;
    my $inetdcontents = "";
    
    # get out of here if inetd.conf doesn't exist
    return 1 if(!-e $file);
    
    open(IN,"<$file") or croak("Couldn't open $file for reading.");
    while(<IN>) {
        if(s/^rsync/\#rsync/) {
            $rsyncfound = 1;
        }
        $inetdcontents .= $_;
    }
    close(IN);
    
    return 1 unless($rsyncfound);
    
    #
    # Backup original inetd.conf file -BEF-
    #
    move($file,"$file.bak.$$") or die("Couldn't move $file to $file.bak.$$");

    # 
    # Write changes to inetd.conf without rsync entry. -BEF-
    #
    open(OUT,">$file") or croak("Couldn't open $file for writing.");
    print OUT $inetdcontents;
    close(OUT);

    # 
    # restart inetd without rsync enabled. -BEF-
    #
    print "Signaling inetd to restart...\n" unless($quiet);
    killall('inetd',1); # sends SIGHUP to all inetd processes

    #
    # Put rsync entry back, so that we leave the system as we found it.
    # Won't take effect though until system is rebooted, or inetd 
    # restarted again. -BEF-
    #
    move("$file.bak.$$",$file) or die("Couldn't move $file.bak.$$ to $file");
    return 1;
}

sub add_rsync_services {
    my $file = "/etc/services";
    open(IN,"<$file") or croak("Couldn't open $file for reading");
   
    my @services = <IN>;
    close(IN);
    return 1 if(grep(/^rsync/,@services));

    backup_file("$file") or croak("Couldn't back up file $file.");
    
    open(OUT,">>$file") or croak("Couldn't open $file for appending");
        print OUT qq(rsync           873/tcp                         # rsync\n);
        print OUT qq(rsync           873/udp                         # rsync\n);
    close(OUT);
    return 1;
} 

sub backup_file {
    my $file = shift;
    my $newfile = $file . $backup_extension;
    if(-e $newfile) {
      print "Not backup up $file to $newfile\n"; 
      print "  because $newfile already exists.\n";
      return 1;
    }
    
    if(!$quiet) {
        print "Backing up $file to $newfile....\n";
    }
    return copy($file,$newfile);
}

sub create_rsyncd_conf {
    my $file = shift;
    open(OUT,">$file") or croak("Couldn't open file $file");
    print OUT <<EOF;
#
# "SystemImager"
#
#  This file: $rsyncd_conf_file
#
list = yes
timeout = 900
dont compress = *.gz *.tgz *.zip *.Z *.ZIP *.bz2 *.deb *.rpm *.dbf
uid = root
gid = root
hosts allow = $hosts_allow

[root]
    path = /

EOF

  close(OUT);
}


sub killall {
    my ($pname,$signal) = @_;
    my @list = split(/\s+/,`pidof $pname`);
    if(scalar(@list)) {
        kill $signal, @list;
    }
}


sub devfs_transform {
    my $devfsentry = shift;
    my ($type, $host, $bus, $target, $lun, $part) = split(/\//,$devfsentry);
    # get rid of the keywords in the sections
    $bus =~ s/\D+//g;
    $target =~ s/\D+//g;
    $part =~ s/\D+//g;
    my $realentry = "hd";
    my $total = $bus * 2 + $target;

    # now we add the real entry... remembering that chr(97) == 'a'
    $realentry .= chr(97 + $total);
    # add the partition number.  $part should always be blank, but
    # it is here for completeness sake
    $realentry .= $part;

    return $realentry;
}


################################################################################
#
# Description:
# Produce a list of IP addresses from a host name.
#
# Usage:
# my @ips = get_ips($hostname);
sub get_ips {

        use Socket;
        use Net::hostent;

        my $host = $_[0];
        
        my ($hinfo, @ips);
        if ( $hinfo = gethost($host) ) { 
            foreach my $addr ( @{$hinfo->addr_list} ) {
                push @ips, inet_ntoa($addr);
            }
        } else {
            die "Can't find an IP address for $host!\n";
        }
        
        return @ips;
}


### END functions


# /* vi: set filetype=perl ai et ts=8: */
