#! /usr/bin/perl -I.
#
# @(#)poc-installer.pl
# (C) The Virtual Laboratory for e-Science Project 2004
#     disclaimers apply, refer to documentation before using this product
#
use Getopt::Long;

$prodname="VL-E PoC release 0.1";

$0 =~ s/.*\///;
$Getopt::Long::ignorecase=0;
$opt_v=0;
@optdef=qw ( noint|q|quiet 
	nc|noconfigure|installonly
	ni|noinstall|configureonly
	baseurl=s conf|c=s dialog=s 
	wget=s tmp|tmpdir=s v:i filelist=s );
&GetOptions(@optdef);

# setting default values if needed
$opt_dialog="dialog" unless $opt_dialog;
$opt_wget="wget" unless $opt_wget;
$opt_conf="poc.conf" unless $opt_conf;
$opt_tmp=$ENV{"TMPDIR"} unless $opt_tmp;
$opt_tmp=$ENV{"TEMP"} unless $opt_tmp;
$opt_tmp="/tmp" unless $opt_tmp;
$opt_baseurl="http://www.dutchgrid.nl/install/poc" unless $opt_baseurl;
$opt_filelist="filelist" unless $opt_filelist;

&init_get_wget;					# we MUST have wget

# create our cache directory for downloads and temporary files
$wd="$opt_tmp/poc-installer-$$";
&Debug(3,"Creating working directory $wd");
mkdir $wd,0700 or die "Cannot create temporary install directory $wd: $!\n";
chdir $wd;

eval "use Config::IniFiles";
"$@" and do {
  &Debug(2,"Obtaining IniFiles.pm from the web in $wd");
  &wget(1,"-N","IniFiles.pm");
  push @INC,"$wd";
  eval "use IniFiles";
};
-f "$opt_conf" or &wget(1,"-N","$opt_conf");

my $cfg = new Config::IniFiles( -file => $opt_conf , -allowcontinue => 1 );
print "The value is " . $cfg->val( 'download', 'file' ) . "."
      if $cfg->val( 'download', 'file' );


# use defaults from config file unless set via arguments
$opt_noint=$cfg->val('init','nointeractive') unless $opt_noint!=undef;
$opt_nc=$cfg->val('init','noconfigure') unless $opt_nc!=undef;
$opt_ni=$cfg->val('init','noinstall') unless $opt_ni!=undef;

&Debug(1,"Interaction settings: noint=$opt_noint, nc=$opt_nc, ni=$opt_ni");

# get dialog programme if needed
$opt_dialog=&init_get_dialog($opt_dialog) unless $opt_noint;

&installer() unless $opt_ni;
&doconfigure() unless $opt_nc;

&msgText(1,
  "The $prodname installation has finished and this installer will now exit."
);

1;


##############################################################################
# INSTALLER section subroutines
##############################################################################
# 

sub installer() {
  my @packagelist;      # ordered list of packagegroup names

  # find or obtain the yum program. User interaction is needed in
  # case the baseOS does not support yum by default
  # yum RPMs are available from the web, via "yumlist"

  system("which --skip-alias yum > /dev/null 2>&1") and do {
    my %yumrpmsel,%yumrpm,$rpmname;

    &msgText(1,
           "Cannot find \"yum\" in your path. Maybe you are running a ".
           "version of RedHat Linux 9 or below, or native RHEL? ".
           "Please download the appropriate version of yum and install ".
           "that RPM as root on this system");

    # assemble the yum options per operating system
    my @oslist=$cfg->Parameters('yumlist');
    foreach (@oslist) {
      my($url,$state)=split(/\s+/,$cfg->val('yumlist',$_),2);
      &Debug(4,"Added yum $url in $state as $_");
      $state="off" unless $state;
      $yumrpmsel{"$_"}=$state;
      $yumrpm{"$_"}=$url;
    }
    %yumrpmsel=&menuList("Package sets",%yumrpmsel);
    foreach $k ( keys %yumrpmsel ) { 
      if ($yumrpmsel{$k} eq "on") {
        &Debug(3,"Added RPM for yum OS version $k");
        $rpmname=$yumrpm{$k};
      }
    }
    $rpmname or &Fatal("No RPM selected and yum not found. Cannot continue");

    &Debug(3,"Downloading yum rpm from $rpmname");
    &wget(1,"-N","$rpmname");
    $rpmname=~s/.*\///; # download will truncare URL dirname
    if ( -f "$rpmname" ) {
      &msgText(0,"About to verify $rpmname from $wd.");
      print "Running RPM test transaction for $rpmname\n";
      system("rpm --test -i $rpmname");
      &msgText(1,"About to install $rpmname from $wd. Press <escape> to cancel");
      print "RPM install of $rpmname\n";
      system("rpm -ivh $rpmname");
    } else {
      &Fatal("Could not download RPM $rpmname correctly, aborted.");
    }
  };

  # but did installing the RPM help?
  system("which --skip-alias yum > /dev/null 2>&1") and 
    &Fatal("Cannot find yum command, and could not install it from RPM. ".
           "Please download and install yourself.");

  # assemble the package groups and heads
  my @packagelist=$cfg->Parameters('packages');
  my $i=0;
  foreach (@packagelist) {
    my $c=$cfg->val('packages',$_); $c=~s/^\s*//;
    my ($state,$rpmname)=split(/\s+/,$c,2);
    my $itemname=sprintf("%03d:%s",$i++,$_); #maintain sort order
    $choices{$itemname}=$state;
    $package{$itemname}=$rpmname;
    &Debug(5,"Added package $rpmname ($state) to menu as $itemname");
  }

  %choices=&checkList("Package sets",%choices);

  foreach $k ( keys %choices ) { 
    if ($choices{$k} eq "on") {
      &Debug(3,"Added package group $k");
      push @packages,$package{$k};
    } else { &Debug(3,"No package group $k"); }
  }

  #
  # create a yum.conf file for installing the VL-E POC RPMs
  #
  $yumconf=($cfg->val('init','yumconf')?
              $cfg->val('init','yumconf'):"/etc/yum.conf");
  $yumconf=&getText("Your yum configuration file",$yumconf);
  &Debug(3,"Yum configuration is $yumconf");
  open YUMCONF,"$yumconf" or die "Cannot open yum configuration $yumconf: $!\n";
  open VLEYUMCONF,">yum.conf" or die "Cannot open new yum config in $wd: $!\n";
  while (<YUMCONF>) {
    /VLEPOC/ and $haspoc++;
    /pkgpolicy.*=.*newest/ and $_="pkgpolicy=last\n";
    print VLEYUMCONF $_;
  };
  close YUMCONF;
  if ( $haspoc ) {
    &msgText(1,"Your $yumconf file seems to have the $prodname repository ".
         "as a standard component. If your yum configuration is outdated, ".
         "strange results may ensue.\nPress escape to cancel this install.");
  } else {
    # add stuff only if we have some basic knowledge of the conf
    print VLEYUMCONF "# VLEPOC sections follow\n";
    my @sections=split(/\s+/,$cfg->val('init','yumsections'));
    open F,$cfg->val('init','yumosnamefile') or 
      &Fatal("Cannot open OS release file ".
               $cfg->val('init','yumosnamefile').": $!");
    chomp(my $osname=<F>);
    close F;

    foreach $sec ( @sections ) {
      my $oscond=$cfg->val("yum:$sec",'oscondition');
      &Debug(4,"For yum section $sec: condition is $oscond to match $osname");
      $oscond and ( $osname=~/$oscond/ or next );
      if ($cfg->val("yum:$sec",'yumurl') and $cfg->val("yum:$sec",'yumname')) {
        my $gpgcheck=($cfg->val("yum:$sec",'yumgpgcheck')?
                      $cfg->val("yum:$sec",'yumgpgcheck'):0);
        print VLEYUMCONF "[$sec]\n";
        print VLEYUMCONF "name=".$cfg->val("yum:$sec",'yumname')."\n";
        print VLEYUMCONF "baseurl=".$cfg->val("yum:$sec",'yumurl')."\n";
        print VLEYUMCONF "gpgcheck=".$gpgcheck."\n";
      } else { 
        &Fatal("attributes yumurl or yumname in yum:$sec missing in $opt_conf");
      }
    }
  }
  close VLEYUMCONF;
  &Debug(3,"Written new yum.conf file");

  # known bugs and work-arounds
  -f "/etc/java/java.conf" and do {
    chomp(my $jrpm=`rpm -qf /etc/java/java.conf`);
    if ( $jrpm !~ /j2sdk_profile-/ ) {
      print "Found /etc/java/java.conf, which may conflict with a package\n";
      print "to be installed. This file is owned by $jrpm.\n";
      print "It may be that a packaged java installation already exists on\n";
      print "this host, so be prepared to repair /etc/java/java.conf\n";
      print "after this installation is complete (test using edg-rm)\n";
      print "Consider removing this RPM and RPMs depending on it, and\n";
      print "exit this shell when done (by typing \"exit\" on the prompt)\n\n";
      system("/bin/sh");
    }
  };

  print "\nStart installing RPM packages using yum, please wait\n";

  foreach $p ( @packages ) { $packages.=" $p"; }
  &Debug(2,"Calling yum with packages $packages");
  system("yum -c ./yum.conf install $packages") and 
    &Warn("Installation of packages may have failed: $!");
}

##############################################################################
# CONFIGURATOR section subroutines
##############################################################################
# 

sub doconfigure() {

  my $prefix=$cfg->val('common','installprefix');
  $prefix="/opt/edg" unless $prefix;

  # first create the relevant directories, if specified in the
  # configuration file
  foreach ( $cfg->val('conf','mkdir') ) {
    my ($dirname,$mode)=split(/\s+/,$_);
    -d $dirname or (mkdir $dirname,$mode or &Fatal("Cannot create $dirname"));
  }

  # do we need to preserve some files
  my $pdir=$cfg->val('conf','preservedir');
  $pdir and do {
    foreach ( $cfg->val('conf','preserve') ) {
      s/\s+.*$//; s/\$CONF/$opt_conf/; system("cp -p $_ $preservedir/");
    }
  };

  # is there a reason to add stuff to ld.so.conf?
  open LD,"/etc/ld.so.conf" and do {
    my %ldlines, $newlines;
    while(<LD>) { chomp($_); $ldlines{$_}++; } 
    close LD;
    foreach ( $cfg->val('conf','ldconfig') ) {
      $ldlines{$_} and next;
      ! -d $_ and next; #makes no sense if not there
      $newlines.="$_\n";
    }
    $newlines ne "" and do {
      &msgText(0, "Adding library paths to /etc/ld.so.conf");
      open LD,">>/etc/ld.so.conf" and print LD $newlines and close LD;
      system("ldconfig");
    };
  };


  # get list of VOs and infrastructures to join
  my $i;
  my @vos=split(/\s+/,$cfg->val('common','vo'));
  foreach $vo ( @vos ) {
    my $vosec="vo:".$vo;
    &Debug(4,"Adding VO $vo to list of potential VOs");
    my @items=$cfg->Parameters($vosec);
    my %cfg=();
    foreach $attr ( @items ) {
      $cfg{$attr}=$cfg->val($vosec,$attr);
      &Debug(6,"For VO $vo added $attr=".$cfg{$attr});
    }
    $cfg{"serial"}=$i++;
    $items{$vo}=\%cfg;
  }

  %choices=();
  foreach $vo ( keys %items ) {
    my ($menutext)=sprintf("%03d:%s", 
                      ($items{$vo})->{"serial"},($items{$vo})->{"name"} );
    $choices{$menutext}=($items{$vo})->{"status"};
    $indices{$menutext}=$vo;
  }
  %choices=&checkList("Supported VOs and Infrastructures",%choices);
  foreach $vo ( keys %choices ) {
    ($choices{$vo} eq "on") and do {
      &Debug(3,"Adding VO $vo at index ".$indices{$vo});
      $supported{$indices{$vo}}=$items{$indices{$vo}};
    };
  }



  # do we want this host to act as a server as well?
  my $isservicehost=$cfg->val('common','servicehost');
  $isservicehost=0 if $isservicehost==undef;
  $isservicehost=&getYesNo("Do you want permit VO access to this host?",
                           $isservicehost);

  $isservicehost eq "yes" and do {
    -f $cfg->val('common','gridmapfile') and do {
      my $presrv=&getYesNo("Do you want to re-use your existing gridmapfile?",
                 $cfg->val('conf','preservemapfile') );
      $presrv eq "yes" and do {
        system("cat ".$cfg->val('common','gridmapfile')." >> ".
                     $cfg->val('common','gmflocal') ) and 
        &Fatal("Cannot append grid-mapfile to ".$cfg->val('common','gmflocal').
               ": error: ".$!);
      };
    };
    &write_mkgridmap_conf($cfg->val('common','mkgridmapconf'),%supported);
    &write_globuscfg($cfg->val('conf','preservedir')."/globus.cfg");

    &msgText(0, "Adding grid-mapfile retrieval to the cron.d directory");
    open CRONTAB,">/etc/cron.d/mkgridmap" or 
      &Fatal("Cannot open>mkgridmap cron");
    print CRONTAB "# edg-mkgridmap cron.d\n";
    print CRONTAB int(rand(59))." 1,7,13,19 * * * ".
                    "$prefix/sbin/edg-mkgridmap --safe ".
                    " --output=".$cfg->val('common','gridmapfile').
                    " --conf=".$cfg->val('common','mkgridmapconf')."\n";
    close CRONTAB;
  };

  &write_voconfig("$prefix/etc",%supported);

  -f "$prefix/etc/cron/edg-fetch-crl-cron" and do {
    &msgText(0, "Adding periodic CRL retrieval to the cron.d directory");
    open CRONTAB,">/etc/cron.d/fetch-crl-cron" or
      &Fatal("Cannot open > fetch-crl cron");
    print CRONTAB "# fetch-crl cron.d\n";
    print CRONTAB int(rand(59))." 1,7,13,19 * * * ".
                  "$prefix/etc/cron/edg-fetch-crl-cron\n";
    close CRONTAB;
  };

  &msgText(0, "Adding basic system configuration in /etc/sysconfig");
  &write_globus_config("/etc/sysconfig/globus");
  &write_edg_config($prefix,"/etc/sysconfig/edg");

  &write_edg_wl_var_config("$prefix/etc/edg_wl_ui_cmd_var.conf");

  -f "$prefix/sbin/edg-replica-manager-configure" and
    &write_edgrm_config("$prefix/etc/edg-replica-manager/edg-replica-manager.conf.poc");

}

# ---------------------------------------------------------------------------
# support routines for the configurator
#


sub write_voconfig() {
  my ($path,%volist) = (@_);
  foreach $vo ( keys %volist ) {
    %vinfo=%{$volist{$vo}};
    &Debug(2,"Adding $vo (".$vinfo{"name"}.")");
    ( $vinfo{"ns"} and $vinfo{"lb"} ) or next;

    mkdir "$path/".$vinfo{"alias"},0755 or next;
    open WL,">$path/".$vinfo{"alias"}."/edg_wl_ui.conf" or do { 
      &Warn("Cannot write to file for $alias"); next;
    };
    print WL "# This file is $path/".$vinfo{"alias"}."/edg_wl_ui.conf\n";
    print WL "# For the VO ".$vinfo{"name"}."\n";
    print WL "[\nVirtualOrganisation = \"".$vinfo{"alias"}."\";\n";
    print WL "NSAddresses = \"".$vinfo{"ns"}."\";\n";
    print WL "LBAddresses = \"".$vinfo{"lb"}."\";\n";
    print WL "#HLRLocation is for accounting purposes\n";
    print WL "#HLRLocation = \"unknown\"\n";
    print WL "MyProxyServer = \"".$vinfo{"myproxy"}."\";\n";
    print WL "]\n";
    close WL;
  }

}

sub write_mkgridmap_conf() {
  my ($name,%volist) = (@_);

  open CONF,">$name" or &Fatal("Cannot open $name for writing mkgridmap.conf");
  print CONF "# edg-mkgridmap.conf generated from poc-configurator.pl\n";
  foreach $vo ( keys %volist ) {
    %vinfo=%{$volist{$vo}};
    &Debug(2,"Adding $vo (".$vinfo{"name"}.")");
    print CONF "# VO $vo (".$vinfo{"name"}.")\n";
    $vinfo{"group"} and print CONF "group ".$vinfo{"group"}." ".
                                   $vinfo{"account"}."\n";
    $vinfo{"auth"} and print CONF "auth ".$vinfo{"auth"}."\n";
  }
  print CONF "# Local grid-mapfile to import and overide all the above.\n";
  print CONF "# eg, gmf_local /opt/edg/etc/grid-mapfile-local\n";
  $cfg->val('common','gmflocal') and 
    print CONF "gmf_local ". $cfg->val('common','gmflocal') ."\n";
  print CONF "\n\n";
}

sub write_globus_config() {
  my ($f) = (@_);

  open CFG,">$f" or 
    &Fatal("globus_config: Cannot open > $f");
  print CFG "# Written by poc-configurator.pl to $f\n";
  print CFG "GLOBUS_LOCATION=".$cfg->val('common','globuslocation')."\n";
  print CFG "GLOBUS_CONFIG=/etc/globus.conf\n";
  print CFG "# make sure to reconfigure this by hand as needed!\n";
  print CFG "GLOBUS_TCP_PORT_RANGE=\"".$cfg->val('common','tcprange')."\"\n";
  close CFG;

  # need to run /opt/globus/sbin/globus-initialization.sh afterwards
  my $globuslocation=$cfg->val('common','globuslocation');
  $globuslocation="/opt/globus" unless $globuslocation;
  [ -f "$globuslocation/sbin/globus-initialization.sh" ] and do {
    system("$globuslocation/sbin/globus-initialization.sh") and
      &Warn("Cannot run globus initialisation script: $!\n".
             "  Name: $globuslocation/sbin/globus-initialization.sh\n");
  };
}

sub write_globuscfg() {
  my ($f) = (@_);

  my $globusloc=$cfg->val('common','globuslocation');
  my $certdir=$cfg->val('common','hostcertdir');
  my $certname=$cfg->val('common','hostcertname');
  my $keyname=$cfg->val('common','hostkeyname');
  my $gridmap=$cfg->val('common','gridmapfile');
  my $mapdir=$cfg->val('common','gridmapdir');

  open CFG,">$f" or 
    &Fatal("globuscfg: Cannot open > $f");
  print CFG <<EOF;
# Written by poc-configurator.pl to $f
[common]
GLOBUS_LOCATION=$globusloc
globus_flavor_name=gcc32dbg
x509_user_cert=$certdir/$certname
x509_user_key=$certdir/$keyname
gridmap=$gridmap
gridmapdir=$mapdir

[gridftp]
log=/var/log/globus-gridftp.log

# these sections are not used
[mds]
globus_flavor_name=gcc32dbgpthr
user=edginfo

[mds/gris/provider/edg]

[mds/gris/provider/gridice]

[mds/gris/registration/site]
regname=mysitelogicalname
reghn=localgrishost.example.org

[gatekeeper]
default_jobmanager=fork
job_manager_path=\$GLOBUS_LOCATION/libexec
jobmanagers="fork"

[gatekeeper/fork]
type=fork
job_manager=globus-job-manager

EOF
  close CFG;
}

sub write_edg_config() {
  my ($prefix,$f) = (@_);
  open CFG,">$f" or 
    &Fatal("edg_config: Cannot open > $f");
  print CFG "# Written by poc-configurator.pl to $f\n";
  print CFG "EDG_LOCATION=$prefix\n";
  print CFG "EDG_WL_LOCATION=$prefix\n";
  print CFG "EDG_LOCATION_VAR=$prefix/var\n";
  print CFG "EDG_TMP=/tmp\n";
  close CFG;
}

sub write_edg_wl_var_config() {
  my ($f) = (@_);
  open CFG,">$f" or 
    &Fatal("edg_wl_var_config: Cannot open > $f");
  print CFG "# Written by poc-configurator.pl to $f\n";

  my $logdest=&getText("Brokered job service logging destination",
                       $cfg->val('common','logdest'));
  my $vo=&getText("Default VO to submit for (for clarity use \"unspecified\")",
                       $cfg->val('common','defaultvo'));

  print CFG <<EOF;
[
rank = - other.GlueCEStateEstimatedResponseTime;
requirements = other.GlueCEStateStatus == "Production";
RetryCount = 3;
ErrorStorage = "/tmp";
OutputStorage = "/tmp/jobOutput";
ListenerPort = 44000;
ListenerStorage = "/tmp";
LoggingTimeout = 30;
LoggingSyncTimeout = 30;
LoggingDestination = "$logdest";
# Default NS logger level is set to 0 (null)
# max value is 6 (very ugly)
NSLoggerLevel = 0;
DefaultLogInfoLevel = 0;
DefaultStatusLevel = 0;
DefaultVo = "$vo";
]
EOF
  close CFG;
}

sub write_edgrm_config() {
  my ($f) = (@_);
  open CFG,">$f" or 
    &Fatal("Cannot open > $f");

  my $iiinfo=&getText("Location of the top-level information service",
                       $cfg->val('common','iilocation'));
  my ($iihost,$iiport)=split(/:/,$iiinfo,2);
  $iiport=2170 unless $iiport;
  $iihost="boswachter.nikhef.nl" unless $iihost;

  my $closese=&getText("Hostname of your closest Storage Element",
                       $cfg->val('common','closese'));
  my $closece=&getText("Hostname of your closest Computing Element",
                       $cfg->val('common','closece'));

  my $localdomain=$cfg->val('common','localdomain');
  chomp($localdomain=`dnsdomainname`) unless $localdomain;
  $localdomain="localdomain" unless $localdomain;

  my $prefix=$cfg->val('common','installprefix');

  print CFG <<EOF;
\@EDG.LOCATION\@|$prefix|location of edg the directory
\@LOCALDOMAIN\@|$localdomain|the local domain
\@DEFAULT.SE\@|$closese|the host of the close SE
\@DEFAULT.CE\@|$closece|the host of the close CE
\@INFOSERVICE\@|MDS|The info provider to use. It can be Stub, MDS or RGMA
\@RLS.MODE\@|LrcOnly|The mode the RLS should be run in. LrcOnly or WithRli
\@STUBFILE\@||The properties file for the static file 'info service'
\@MDS.HOST\@|$iihost|The host of the MDS info provider
\@MDS.PORT\@|$iiport|The port of the MDS info provider
\@ROS.FAILURE\@|false|Fail if no ROS is available
\@CONF.GCC\@|_gcc3_2_2|The gcc suffix as used on the build box (empty for 2.95, _gcc3_2_2 for 3.2.)
EOF
  close CFG;

  &msgText(0,"Running the edg-replica-manager configurator");
  system("$prefix/sbin/edg-replica-manager-configure $f");
}


# @(#)poc-common.pl -- SECTION COMMON TO POC INSTALLER AND CONFIGURATOR
##############################################################################
#
# dialog printing meta-functions
#
sub getText() {
  my ($title,$default) = (@_);
  my ($msg);

  $opt_noint and return $default;

  open DLG,"$opt_dialog --inputbox \'$title\' 6 70 $default 2>&1 1>/dev/null |";
  $msg=<DLG>;
  close DLG;
  $? and &Fatal("User terminated input dialog box, exiting.");
  chomp($msg);
  return $msg;
}

sub getYesNo() {
  my ($title,$default) = (@_);
  my ($msg);

  $opt_noint and do { $default="yes" unless $default; return $default };
  $default="yes" if ($default eq "true" or $default == 1);

  open DLG,"$opt_dialog --cr-wrap --yesno \'$title\' 12 70 2>&1 1>/dev/null |";
  $msg=<DLG>;
  close DLG;
  &Debug(4,"YesNo resulted in $?");
  if ($? == 0) { return "yes"; }
  elsif ($? <= 256 ) { return "no"; }
  else { &Fatal("User terminated input dialog box, exiting.");}
}

sub msgText() {
  my ($persist,$title,$default) = (@_);
  my ($boxtype);

  $opt_noint and return 0;

  if ( $persist ) { $boxtype="msgbox"; } else { $boxtype="infobox"; }
  open DLG,"$opt_dialog --cr-wrap --$boxtype \'$title\' 12 70 2>&1 1>/dev/null|";
  $msg=<DLG>;
  close DLG;
  $? and &Fatal("User terminated input dialog box, exiting.");
  return 0;
}

sub checkList() {
  my ($title,%choices) = (@_);
  my ($cmd,$i,$k);
  my ($msg,$text,@list);

  $opt_noint and return %choices;

  $cmd="$opt_dialog --checklist \"$title\" 16 70 11 ";
  $i=1; 
  foreach $k ( sort keys %choices ) { 
    $list[$i]=$k;
    ($text=$k)=~s/.*://;
    $cmd.=$i++." \"".$text."\" ".$choices{$k}." "; 
  }
  &Debug(4,$cmd);
  open DLG,"$cmd 2>&1 1>/dev/null |"; $msg=<DLG>; close DLG;
  $? and &Fatal("User terminated input dialog box ($?), exiting.");
  chomp($msg);
  &Debug(4,"checkList result: <$msg>");
  $msg=~s/"//g;
  split(/\s+/,$msg);

  foreach $k ( keys %choices ) { $choices{$k}="off"; }
  foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; }

  return %choices;
}

sub menuList() {
  my ($title,%choices) = (@_);
  my ($cmd,$i,$k);
  my ($msg,$text,@list);

  $opt_noint and return %choices;

  $cmd="$opt_dialog --menu \"$title\" 16 70 11 ";
  $i=1; 
  foreach $k ( sort keys %choices ) { 
    $list[$i]=$k;
    ($text=$k)=~s/.*://;
    $cmd.=$i++." \"".$text."\" "; 
  }
  &Debug(4,$cmd);
  open DLG,"$cmd 2>&1 1>/dev/null |";
  $msg=<DLG>;
  close DLG;
  $? and &Fatal("User terminated input dialog box, exiting.");
  chomp($msg);
  &Debug(4,"menuList result: <$msg>");
  $msg=~s/"//g;
  split(/\s+/,$msg);

  foreach $k ( keys %choices ) { $choices{$k}="off"; }
  foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; }
  return %choices;
}

sub radioList() {
  my ($title,%choices) = (@_);
  my ($cmd,$i,$k);
  my ($msg,$text,@list);

  $opt_noint and return %choices;

  $cmd="$opt_dialog --menu \"$title\" 16 70 11 ";
  $i=1; 
  foreach $k ( sort keys %choices ) { 
    $list[$i]=$k;
    ($text=$k)=~s/.*://;
    $cmd.=$i++." \"".$text."\" "; 
  }
  &Debug(4,$cmd);
  open DLG,"$cmd 2>&1 1>/dev/null |";
  $msg=<DLG>;
  close DLG;
  $? and &Fatal("User terminated input dialog box, exiting.");
  chomp($msg);
  &Debug(4,"radioList result: <$msg>");
  $msg=~s/"//g;
  split(/\s+/,$msg);

  foreach $k ( keys %choices ) { $choices{$k}="off"; }
  foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; }
  return %choices;
}

sub wget {
  my($ftl,$opt,$url) = @_;
  $url=~/^http:/ or $url=$opt_baseurl."/".$url;
  system("$opt_wget -q $opt $url") and do {
    if ($ftl) { &Fatal("Cannot download $url using $opt_wget"); }
    else { &Warn("Cannot download $url using $opt_wget"); }
  };
}

##############################################################################
#
# Initialisation and web routines
#

# ----------------------------------------------------------------------------
# void init_get_wget()
# Function:
#      test whether the "wget" program is there, or abort
# Dependencies:
#      "which" must exist in current $PATH
#
sub init_get_wget() {
  system("which --skip-alias $opt_wget > /dev/null 2>&1") and do {
    print STDERR "Cannot locate the wget programme as $opt_wget\n";
    print STDERR "You MUST install wget before installing the $prodname\n";
    print STDERR "\n";
    print STDERR "If you have wget installed but invoked with a different\n";
    print STDERR "name use the \"--wget=<name>\" argument to the installer\n";
    exit(1);
  };
}

# ----------------------------------------------------------------------------
# string init_get_dialog(dialog_guess)
# Function:
#        test whether the guessed dialog location exists or, if not,
#        obtain a copy of the dialog programme from the web
# Dependencies:
#        &wget() subroutine
#        $wd - temporary installer cache area
#        &Debug() subroutine
#
sub init_get_dialog() {
  my ($dialog) = (@_);
  # verify we have the dialog or Xdialog programme, or obtain from web
  system("which --skip-alias $dialog > /dev/null 2>&1") and do {
    $dialog="Xdialog";
    system("which --skip-alias $dialog > /dev/null 2>&1") and do {
      &wget(1,"-O $wd/dialog","dialog");
      $dialog="$wd/dialog";
      chmod 0755,$dialog;
      &Debug(4,"Dialog downloaded from $opt_baseurl/dialog");
    };
  };
  &Debug(3,"Dialog programme path set to $dialog");
  return $dialog;
}


##############################################################################
#
# Debug and error functions
#
sub Fatal {
  my ($msg)=@_;
  print STDERR "Fatal error encountered.\n$msg\n";
  exit(1);
}
sub Warn {
  my ($msg)=@_;
  print STDERR "Warning: $msg\n";
}
sub Debug {
  my ($lvl,@msg)=@_;
  $lvl<=$opt_v and do {
    foreach $l ( @msg ) { print "DBG[$lvl]: $l\n"; }
  };
}
