#!/usr/bin/perl -w
#
#	vmmgr.pl - A tool for packing and unpacking VMWare virtual machine
#	system archives, with optional renaming.
#
#	Copyright (c) 2000 Kyle Amon
#	All rights reserved.
#
#	Author:
#
#		Kyle Amon
#		GNUTEC, Inc.
#       Information Technology Solutions
#		http://www.gnutec.com/
#		amonk@gnutec.com
#		203-668-UNIX
#
#	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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#	Change history:
#		0.10    04/27/00        Alpha release

require 5.004;
use strict;
use Getopt::Long;

#
# User defined variables
#

my $archive	= "/eng/archive";
my $sandbox	= "/eng/sandbox";
my $tmp		= "/eng/tmp";
my $userid	= "eng";
my $group	= "vmware";

#
# Options requiring arguments
#

my $src		= '';
my $dst		= '';

#
# Main symbol declerations
#

my (
     $pack, $unpack, $remove, $help, $debug,
     @files, $file,
     $uid, $gid,
     $line
);

main();
exit;

#
# Main subroutine
#

sub main {

  #
  # Secure the environment
  #

  delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};	# make %ENV safer
  $ENV{PATH} = "/bin:/usr/bin:/usr/sbin";	# set PATH

  #
  # Process command line arguments
  #

  getopts();

  #
  # Get uid and gid for $userid and $group
  #

  ($uid) = (getpwnam ("$userid")) [2];
  ($gid) = (getgrnam ("$group")) [2];

  #
  # Determine whether to pack or unpack an archive
  #

  if ($pack) {
    &pack();
  } elsif ($unpack) {
    &unpack();
  }

}

sub getopts {

  #
  # Process arguments
  #

  GetOptions ('pack'   => \$pack,
              'unpack' => \$unpack,
              'src=s' => \$src,
              'dst=s' => \$dst,
              'remove' => \$remove,
              'help' => \$help,
              'debug' => \$debug);

  #
  # Ensure that --src was specified
  #

  if ($src eq '') {
    help();
  }

  #
  # Ensure that --pack or --unpack was also specified, but not both
  #

  unless (($pack) || ($unpack)) {
    help();
  }

  if (($pack) && ($unpack)) {
    help();
  }

  #
  # If --remove was specified, ensure that --pack was also specified
  #

  if ($remove) {
    unless ($pack) {
      help();
    }
  }

  #
  # Remove absolute path if specified
  #

  $src =~ s/.*\///;
  $dst =~ s/.*\///;

  #
  # Remove tar.gz extensions if specified
  #

  $src =~ s/\.tar\.gz//;
  $dst =~ s/\.tar\.gz//;

  #
  # Set --dst to default if unspecified
  #

  if ($dst eq '') {
    $dst = $src;
  }

  #
  # Debug info
  #

  print "src = $src", "\n" if ($debug);
  print "dst = $dst", "\n" if ($debug);

}

sub pack {

  #
  # Move src directory to $tmp
  #

  system "/bin/mv", "$sandbox/$src", "$tmp"
      || die "mv failed: $!\n";

  #
  # Rename src directory and files if requested
  #

  if ($dst ne $src) {
    renamevm();
  }

  #
  # Pack source directory to destination archive
  #

  if ($dst ne $src) {
    system "/bin/tar", '-C', "$tmp", '-czf', "$archive/$dst.tar.gz", "$dst"
      || die "tar failed: $!\n";
  } else {
    system "/bin/tar", '-C', "$tmp", '-czf', "$archive/$src.tar.gz", "$src"
      || die "tar failed: $!\n";
  }

  #
  # Make sure ownerships and permissions are good
  #

  chown "$uid", "$gid", "$archive/$dst.tar.gz" || die "chown failed: $!\n";
  chmod 0444, "$archive/$dst.tar.gz" || die "chmod failed: $!\n";

  #
  # Remove dst directory from $tmp if requested, else put it back
  #

  if ($remove) {
    system "/bin/rm", '-r', "/$tmp/$dst" 
      || die "rm -r failed: $!\n";
  } else {
    system "/bin/mv", "$tmp/$dst", "$sandbox"
        || die "mv failed: $!\n";
  }

}

sub unpack {

  #
  # Unpack source archive to $tmp directory
  #

  system "/bin/tar", '-C', "$tmp", '-xzf', "$archive/$src.tar.gz"
    || die "tar failed: $!\n";

  #
  # Rename dst directory and files if requested
  #

  if ($dst ne $src) {
    renamevm();
  }

  #
  # Make sure ownerships and permissions are good
  #

  chown "$uid", "$gid", "$tmp/$dst" || die "chown failed: $!\n";
  chmod 02775, "$tmp/$dst" || die "chmod failed: $!\n";

  chdir "$tmp/$dst"
    || die "chdir failed: $!\n";

  @files = listfiles ("$tmp/$dst");
  foreach $file (@files) {
    chown "$uid", "$gid", "$file" || die "chown failed: $!\n";
    if ($file =~ /\.cfg/) {
      chmod 0774, "$file" || die "chmod failed: $!\n";
    } else {
      chmod 0664, "$file" || die "chmod failed: $!\n";
    }
  }

  #
  # Move unpacked vm into place
  #

  system "/bin/mv", "$tmp/$dst", "$sandbox/$dst"
      || die "mv failed: $!\n";

}

sub renamevm {

  system "/bin/mv", "$tmp/$src", "$tmp/$dst"
      || die "mv failed: $!\n";

  chdir "$tmp/$dst"
    || die "chdir failed: $!\n";

  @files = listfiles ("$tmp/$dst");
  foreach $file (@files) {
    $file =~ s/\.(.*)$//;
    rename "$file.$1", "$dst.$1" || die "rename failed: $!\n";
    print "renaming $file.$1 to $dst.$1\n" if ($debug);
  }

  #
  # Change values in .cfg file
  #

  open CFG, "< $tmp/$dst/$dst.cfg";
  flock CFG, 2;		# set exclusive lock
  open CFGTMP, "> $tmp/$dst/$dst.cfg.tmp";

  foreach $line(<CFG>) {
    if ($line =~ /^ide0:0.fileName/) {
      $line =~ s/= .*/= $sandbox\/$dst\/$dst.dsk/;
      print CFGTMP $line;
    } elsif ($line =~ /^nvram/) {
      $line =~ s/= .*/= $sandbox\/$dst\/$dst.nvram/;
      print CFGTMP $line;
    } elsif ($line =~ /^log.fileName/) {
      $line =~ s/= .*/= $sandbox\/$dst\/$dst.log/;
      print CFGTMP $line;
    } else {
      print CFGTMP $line;
    }
  }

  close CFGTMP;
  close CFG;

  rename "$tmp/$dst/$dst.cfg.tmp", "$tmp/$dst/$dst.cfg"
    || die "rename failed: $!\n";
   
# After making sure no VMs are running
#config=`grep mru.config /home/${user}/.vmware/preferences | sed 's/:"//'`
#sed "s/^mru.config.*:\"/${config}:\"/" /home/${user}/.vmware/preferences > /tmp/zout

}

sub listfiles {

  opendir(DIR, "$_[0]") || die "can't opendir $_[0]: $!";
  @files = grep { /[^\.]/ && -f "$_[0]/$_" } readdir(DIR);
  closedir DIR;
  return @files;

}

sub help {
  print "usage: vmmgr.pl --pack --unpack --src source [--dst destination]\n";
  print "		[--remove] [--help] [--debug]\n\n";
  print "	--pack		pack a vm archive\n";
  print "	--unpack	unpack a vm archive\n";
  print "	--src		source file or directory\n";
  print "	--dst		destination file or directory\n";
  print "	--remove	remove source directory after packing\n";
  print "	--help		display this help\n";
  print "	--debug		print debugging information\n\n";
  print "  vmmgr.pl is a tool for managing VMWare virtual machines.  It archives\n";
  print "  or unarchives a virtual machine, renaming it via the --dst option,\n";
  print "  if specified.  The --src option must always be specified along with\n";
  print "  either --pack or --unpack.  When used with --pack, --src indicates the\n";
  print "  directory to pack.  When used with --unpack, --src indicates the .tar.gz\n";
  print "  file of the virtual machine to unpack.  If --dst is unspecified, the\n";
  print "  destination will be the file, source.tar.gz, when packing and the\n";
  print "  directory, source, when unpacking.  If --remove is used with --pack,\n";
  print "  the source directory will be removed after it has been packed.\n\n";
  print "  Examples:\n\n";
  print "  vmmgr.pl --pack --src somemachine\n\n";
  print "  would pack the the virtual machine, /eng/sandbox/somemachine\n";
  print "  into the archive, /eng/archive/somemachine.tar.gz, leaving the\n";
  print "  virtual machine, /eng/sandbox/somemachine, where it is.\n\n";
  print "  vmmgr.pl --pack --remove --src somemachine\n\n";
  print "  would pack the the virtual machine, /eng/sandbox/somemachine\n";
  print "  into the archive, /eng/archive/somemachine.tar.gz, removing the\n";
  print "  virtual machine, /eng/sandbox/somemachine.\n\n";
  print "  vmmgr.pl --pack --src somemachine --dst othermachine\n\n";
  print "  would pack the the virtual machine, /eng/sandbox/somemachine\n";
  print "  into the archive, /eng/archive/othermachine.tar.gz, leaving the\n";
  print "  virtual machine, /eng/sandbox/somemachine, where it is.\n\n";
  print "  vmmgr.pl --unpack --src somemachine\n\n";
  print "  would unpack the the virtual machine archive,\n";
  print "  /eng/archive/somemachine.tar.gz into the virtual machine,\n";
  print "  /eng/sandbox/somemachine.\n\n";
  print "  vmmgr.pl --unpack --src somemachine --dst othermachine\n\n";
  print "  would unpack the the virtual machine archive,\n";
  print "  /eng/archive/somemachine.tar.gz into the virtual machine,\n";
  print "  /eng/sandbox/othermachine.\n\n";
  exit;
}
