#!/usr/bin/perl -T -w
delete $ENV{$_} for qw(PATH BASH_ENV ENV CDPATH);
# The above line and a "T" switch on the top line enable taint
# mode.  You may add and remove these as desired.
#
# QuickSwitch
# Switch network profiles on the fly
# Copyright (c) 2000 Mohit Muthanna 
#
# Some modifications by Paul Seamons
#
# 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.
#
# Send bug-reports, comments, women to bugs@muthanna.com
#
# Also see /etc/switchto.conf for further docs.
#
# Release: 0.16
#
# $Log: switchto,v $
# Revision 1.17  2003/01/23 16:07:59  muthanna
# - Taint mode warning on some systems fix.
#
# Revision 1.16  2003/01/23 16:03:54  muthanna
# - Documentation, help text and version updates.
#
# Revision 1.15  2003/01/23 15:46:47  muthanna
# - Fixed routing bugfix. Specifies interface to use in routing
#   table.
#
# Revision 1.14  2003/01/22 21:53:54  muthanna
# - Version update.
# - README update.
#
# Revision 1.13  2003/01/22 21:51:01  muthanna
# - Fixed some DHCP problems which a lot of users were
#   reporting.
# - Added a dhcpkill option, to kill the dhcp client before
#   switching to a non-dhcp profile.
#
# Revision 1.12  2002/01/15 20:28:45  rhandom
# - Compatibility changes for switcher
#
# Revision 1.11  2002/01/15 19:07:02  rhandom
# - Added --help and --version
# - Some code cleanup and comments
#
# Revision 1.10  2002/01/15 18:05:02  rhandom
# - Major code switch to Object Oriented model
# - More uniform program flow in all modes of operation
# - Taint, strict, and warn clean safe structure
# - Includes symlinking fix of 0.14.2
# - Checks permissions of conf file
# - Added setuid ability with security checking
# - No longer allows for running non root
# - Enabled taint mode
# - Moved conf to switchto.conf
#
# Revision 1.9  2001/12/17 18:02:41  muthanna
# - Major symlinking bug fixed. Hosts and DNS files need to be
#   removed before switching profiles.
#
# Revision 1.8  2001/12/13 20:01:06  muthanna
# - Date backup fix.
#
# Revision 1.7  2001/12/13 18:42:08  muthanna
# - Switcher fix for files section.
#
# Revision 1.6  2001/12/13 17:12:31  muthanna
# - Changed file operations from copying to symlinking.
# - Added new ANSI color features.
#
# Revision 1.5  2001/12/07 20:20:07  muthanna
# - Dynamic file switching feature
# - New [files] section, now ignored with -l.
#
# Revision 1.4  2001/12/05 21:41:10  muthanna
# - Wireless bugfix
#
# Revision 1.3  2001/12/05 16:55:11  muthanna
# - Wireless LAN configuration with iwconfig
# - Based on a patch submitted by NeilFred Picciotto
#
# Revision 1.2  2001/12/05 16:18:51  muthanna
# - Now accepts profilename in kernel parameter SWITCHTO.
# - Saves last good known configuration.
# - Can be integrated easily on startup with the -s switch.
# - Based on patch submitted by Stuart Menefy
#
# 
# Last Release: v0.7 Changelog:
#	-Added NIS support
#	-Added DHCP/BOOTP setup
#	-Added description field
#	-Able to now specify an alternate hosts file
#	-Aslo able to specify alternate NFS exports
#	-Some code cleanup
# Last Release: v0.5 Changelog:
# 	-Command Line switching implemented. 
#	 The user now has the option of changing the entire network
#	 configuration via the commandline.
#
# CVS: $Id: switchto,v 1.17 2003/01/23 16:07:59 muthanna Exp $
#
# QuickSwitch
# Copyright (c) 2000 Mohit Muthanna 
#
# Code modifications and 
# Additions by Paul Seamons (perl@seamons.com)
#
# 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.

package QuickSwitch;

use strict;
use vars qw($VERSION $PROGNAME $AUTHOR $DATE
            $CONF_FILE %DEFAULTS);
use Data::Dumper qw(Dumper);

$VERSION   = '1.05';
$PROGNAME  = 'QuickSwitch';
$AUTHOR    = "Mohit Muthanna, Paul Seamons";
$DATE      = '2002';

### I would suggest renaming this /etc/switchto.conf
$CONF_FILE = "/etc/switchto.conf";
%DEFAULTS  = (
              autoconfig      => 0,
              device          => "eth0",
              ifconfig        => "/sbin/ifconfig",
              iwconfig        => "/sbin/iwconfig",
              dhcpconfig      => "/sbin/dhcpcd -n", # newer versions of redhat
              dhcpkill      => "/sbin/dhcpcd -k", # newer versions of redhat
              netstat         => "/bin/netstat",
              route           => "/sbin/route",
              hostnamecmd     => "/bin/hostname",
              date            => "/bin/date",
              mv              => "/bin/mv",
              cp              => "/bin/cp",
              ln              => "/bin/ln",
              rm              => "/bin/rm",
              dnsfile         => "/etc/resolv.conf",
              hostsfile       => "/etc/hosts",
              nfsexportall    => "/usr/sbin/exportfs -a",
              nfsexportsfile  => "/etc/exports",
              niscommand      => "/bin/nisdomainname",
              servicefilename => "/etc/switchto.last",
              execpre         => undef,
              execpost        => undef,

              color_ok     => "32;1",
              color_error  => "31;1",
              color_debug  => "33;1",
              color_logo   => "37;1",
              color_normal => "0",
              );

###----------------------------------------------------------------###

### make sure we have something to go on
if( ! $ENV{RUNNING_FROM_SWITCHER} ){
  if( ! @ARGV ){
    show_usage();
  } else {
    main();
  }
  exit 0;
}

sub main {

  my $self = __PACKAGE__->new();

  ### determine information such as debug level
  $self->parse_commandline_switches;

  ### show a help screen
  if( $self->{show_usage} ){
    show_usage();
    exit(0);
  }

  ### print out some headers
  print "$PROGNAME $VERSION $AUTHOR $DATE\n"
    if ! $self->{quiet_mode};

  if( $self->{show_version} ){
    exit(0);
  }

  ### set default paths to files and programs
  $self->set_config_defaults;
  
  ### see if we are running as a non root user
  $self->check_setuid;

  ### read in all profiles
  $self->parse_conf_file;

  ### list profiles if that is all that is asked
  if( $self->{list_mode} ){
    print "\nAvailable service profiles:\n";
    $self->list_services;
    exit(0);
  }
  


  ### choose the service to use
  $self->get_desired_service;

  ### override defaults with chosen service values
  $self->set_service_values;

  ### override values with commandline arguments
  $self->parse_commandline_overrides;



  ### done reading arguments, let's do it
  $self->configure_job;

  ### write out which service we used
  $self->save_service;

  ### uncomment this to see the objects values
  ### it will only show up in debug output
#  $self->print_debug(Dumper($self));

  ### allow for pausing when done
  if( $self->{pause} && $self->{pause} =~ /^\d+$/ ){
    $self->print_debug("Pausing for $self->{pause} sec.");
    sleep $self->{pause};
  }

}

###----------------------------------------------------------------###

sub new {
  my $class = shift || __PACKAGE__;
  my $self = bless {
    conf_file    => $CONF_FILE,
    command_line => join(" ", $0, @ARGV),
  }, $class;
  return $self;
}

###----------------------------------------------------------------###

### read only the switches
sub parse_commandline_switches {
  my $self = shift;

  my $cmd_line = $self->{command_line};

  ### allow for -d0 -d1 (or -d) -d2
  $self->{verbose}      = defined($1) ? $1 : 1
    if $cmd_line =~ m/\s+-d\s*([012])?/;
  $self->{list_mode}    = 1  if $cmd_line =~ m/\s+-l\b/;
  $self->{use_last}     = 1  if $cmd_line =~ m/\s+-s\b/;
  $self->{backup_files} = 1  if $cmd_line =~ m/\s+-b\b/;
  $self->{autoconfig}   = 1  if $cmd_line =~ m/\s+-a\b/;
  $self->{quiet_mode}   = 1  if $cmd_line =~ m/\s+-q\b/;
  $self->{useansi}      = 1  if $cmd_line !~ m/\s+(-n|--nocolors)\b/;
  $self->{show_version} = 1  if $cmd_line =~ m/\s+(-v|--version)\b/;
  $self->{show_usage}   = 1  if $cmd_line =~ m/\s+(-h|--help)\b/;

  ### allow for -p or -p2 or -p5 etc
  $self->{pause} = defined($1) ? $1 : 2
    if $cmd_line =~ m/\s+-p\s*(\d+)?/;

}

### set up the default values
sub set_config_defaults {
  my $self = shift;

  foreach my $key (keys %DEFAULTS){
    next if exists $self->{$key};
    $self->{$key} = $DEFAULTS{$key};;
  }

  $self->{debug} = 1 if $self->{verbose} && $self->{verbose} >= 2;
  $self->print_debug("Leaving set_config_defaults.");
}

sub check_setuid {
  my $self = shift;
  $self->print_debug("In check_setuid");

  ### check the effective user, real user and mode
  my $mode = (stat($0))[2];
  if( ($mode >> 9 & 07) == 4 ){
    if( $> || $< ){ 
      $> = $< = 0;
      $self->{"running as setuid"} = 1;
      $self->print_debug("Running $0 as setuid - disabling commandline config");
    }
  }else{
    if( $> || $< ){
      $self->humanerr("Error: May not run as non root user.\n");
    }
  }
}

### read in the profiles
sub parse_conf_file {
  my $self = shift;
  $self->print_debug("In parse_conf_file.");

  my $conf = {};
  my $file = $self->{conf_file};

  ### check existance (-T fails for non root user)
  if( ! -e $file ){
    $self->humanerr("Error: Can't find conf file $file\n");
  }

  ### check permissions on the conf file
  my $mode = (stat($file))[2];
  if( ($mode >> 6 & 07 != 6) || ($mode & 077 != 0) ){
    my $error = $self->ANSI_color('error')
      . "Error: File \"$file\" must be chmod 600\n"
      . $self->ANSI_color('normal');
    $self->humanerr($error);
  }

  ### attempt to open it
  if( ! open(F, "<$file") ){
    $self->humanerr("Error: Couldn't open $file [$!]\n");
  }

  my $service = "none";
	
  while( <F> ) {
    chomp;

    # Junk out comments and blank lines
    s/\#.*$//;
    s/^\s+//;
    s/\s+$//;
    next unless length($_);

    # Get service name and populate hash
    $service = $1 if m/^\[(.+)\]$/;
    $conf->{$service}->{lc($1)} = $2 if m/^([^=]+)\s*=\s*(.*?)$/;
  }
  close(F);

  ### overwrite default config values
  if( exists $conf->{config} ){
    foreach my $key (keys %{ $conf->{config} }){
      $self->{$key} = $conf->{config}->{$key};
    }
    delete $conf->{config};
  }

  ### remove the "files" service
  if( exists $conf->{files} ){
    $self->{switch_files} = $conf->{files};
    delete $conf->{files};
  }

  ### remove options in the "none" service
  if( exists $conf->{none} ){
    $self->{non_service_options} = $conf->{none};
    delete $conf->{none};
  }

  ### not sure where this is used right now
  delete $conf->{autodetect};


  ### store all other services for later use
  $self->{services} = $conf;

}

### figure out which service we want to show
sub get_desired_service {
  my $self = shift;
  $self->print_debug("In get_desired_service.");

  if( $self->{use_last} ){

    ### allow for service passed from environment
    ### useful line in lilo such as:
    ### append="SWITCHTO=home"
    if( defined $ENV{SWITCHTO} && $ENV{SWITCHTO} =~ /^(\w+)$/ ){
      print "Reading service from %ENV\n" if ! $self->{quiet_mode};
      $self->{service} = $1;
      return;
    }

    ### allow fall back to last used service
    my $conf_file = $self->{servicefilename};
    print "Checking last service...$conf_file\n" if ! $self->{quiet_mode};
    open(F, "<$conf_file") || die "Cannot open \"$conf_file\" [$!]";
    
    my $service = <F>;
    chomp($service) if defined $service;
    if( ! defined($service) || ! length($service) ){
      die "No service found in $conf_file";
    }
    close(F);
  
    $self->{service} = $service;
    return;
  }

  ### read service from the command line
  $self->{service} = $ARGV[$#ARGV];
  return;
}

### set values from the chosen service profile
sub set_service_values {
  my $self = shift;
  my $service = $self->{service};
  $self->print_debug("In set_service_values.");

  ### see if we have a service for the requested type
  if( ! $self->{autoconfig} ){
    if( ! exists $self->{services}->{$service} ){
      $self->print_ANSI_color('error');
      print "Service not found: \"$service\"\n";
      $self->print_ANSI_color('normal');
      exit 1;
    };
    
    print "Using service: "
      . $self->ANSI_color('logo')
      . "$service\n"
      . $self->ANSI_color('normal')
      if ! $self->{quiet_mode};
  }


  my $ref = $self->{services}->{$service};

  $ref->{ipaddr}  ||= $ref->{address} || $ref->{ip};
  $ref->{domain}  ||= $ref->{domainname};
  $ref->{netmask} ||= "255.255.255.0";

  ### set values from config file
  foreach my $key (qw(execpre     execpost
                      device      hostname
                      fqhostname  ipaddr
                      netmask     gateway
                      domain      search
                      usehosts    nfsexports
                      nisdomain   essid
                      key
                      )){
    $self->{$key} = $ref->{$key} if defined($ref->{$key});
  }  

  ### set nameserver values from config file (dns1, dns2, etc)
  foreach my $key (keys %$ref){
    next unless $key =~ /^dns\d+$/i;
    $self->{lc($key)} = $ref->{$key};
  }

}

### read in any other values from the command line
sub parse_commandline_overrides {
  my $self = shift;
  $self->print_debug("In parse_commandline_overrides.");

  my $cmd_line = $self->{command_line};

  ### make sure no command line options are passed here if setuid
  if( $self->{"running as setuid"} ){
    if( $cmd_line =~ m/\w:/ ){
      $self->humanerr("Error: Only switches and service may be used under setuid.\n");
    }
  }

  if( $cmd_line =~ m/\s+(?:address|ip|ipaddr):(\d+(\.\d+){3}|DHCP)?/i ){
    $self->humanerr("Invalid IP address\n") unless $1;
    $self->{ipaddr} = uc($1);
    print "Got IP \"$self->{ipaddr}\"\n" if $self->{verbose};
  }

  if( $cmd_line =~ m/\s+netmask:(\d+\.\d+\.\d+\.\d+)?/i ){
    if( !$self->{ipaddr} ) { $self->humanerr("Can't have netmask without IP.\n"); } 
    $self->humanerr("Invalid netmask\n") unless length($1);
    $self->{netmask}   = $1;
    print "Got Netmask \"$self->{netmask}\"\n" if $self->{verbose};
  }

  if( $cmd_line =~ m/\s+gw:(\d+\.\d+\.\d+\.\d+)?/i ){
    $self->humanerr("Invalid gateway address\n") unless $1;
    $self->{gateway} = $1;
    print "Got Gateway Address \"$self->{gateway}\"\n" if $self->{verbose};
  }

  if( $cmd_line =~ m/\s+host:([\.\w\-]+)?/i ){
    $self->humanerr ("Invalid host name.\n") unless length($1);
    $self->{hostname} = lc($1);
    print "Got Hostname \"$self->{hostname}\"\n" if $self->{verbose};
  }
  
  if( $cmd_line =~ m/\s+essid:(\S+)?/i ){
    $self->humanerr("Invalid ESSID.\n") unless length($1);
    $self->{essid} = $1;
    $self->{essid} =~ s/_/ /g;
    print "Got ESSID \"$self->{essid}\"\n" if $self->{verbose};
  }
  
  if( $cmd_line =~ m/\s+(?i:key):([0-9a-fA-F]*)?/ ){
    $self->humanerr("Invalid key.\n") unless length($1);
    $self->{key} = $1;
    print "Got key \"$self->{key}\"\n" if $self->{verbose};
  }
  
  if( $cmd_line =~ m/\s+usehosts:(\S+)?/i ){
    $self->humanerr("Invalid host file name.\n") unless length($1);
    $self->{usehosts} = $1;
    print "Got hosts filename \"$self->{usehosts}\"\n" if $self->{verbose};
    $self->humanerr("File does not exist \"$self->{usehosts}\"\n")
      if ! -e $self->{usehosts};
  }

  if( $cmd_line =~ m/\s+fqhost:([\.\w\-]+)?/i ){
    $self->humanerr("Invalid fqhost name.\n") unless length($1);
    $self->{fqhostname} = $1;
    print "Got Fully Qualified Hostname \"$self->{fqhostname}\"\n"
      if $self->{verbose};
  }

  if( $cmd_line =~m/\sdomain:([\.\w\-]+)?/i ){
    $self->humanerr("Invalid domain name.\n") unless length($1);
    $self->{domain} = $1;
    print "Got Domain \"$self->{domain}\"\n" if $self->{verbose};
  }
  
  if( $cmd_line =~m/\sdns1:(\d+\.\d+\.\d+\.\d+)?/i ){
    $self->humanerr("Invalid DNS1.\n") unless length($1);
    $self->{dns1} = $1;
    print "Got DNS1 \"$self->{dns1}\"\n" if $self->{verbose};
  }

  if( $cmd_line =~m/\sdns2:(\d+\.\d+\.\d+\.\d+)?/i ){
    $self->humanerr("Invalid DNS2.\n") unless length($1);
    $self->{dns2} = $1;
    print "Got DNS2 \"$self->{dns2}\"\n" if $self->{verbose};
  }
		
}

###----------------------------------------------------------------###

### spit out a pretty list of available services
sub list_services {
  my $self = shift;

  if( ! keys %{ $self->{services} } ){
    print "No services configured.\n";
  }
    
  foreach my $key (keys %{ $self->{services} } ){
    $self->print_ANSI_color('logo');
    print $key;
    $self->print_ANSI_color('normal');
    print " - $self->{services}->{$key}->{description}\n";
  }
}

###----------------------------------------------------------------###

sub configure_job {
  my $self = shift;
  $self->print_debug("Configuring the Job.\n");

  # Handle execpre
  $self->do_execpre if $self->{execpre};
  
  # Setup wireless LAN configuration
  $self->configure_wireless if $self->{essid} || $self->{key};

  # Setup the device (default is eth0)
  $self->configure_device;

  # Set the gateway
  if( $self->{gateway} ){
    $self->delete_route;
    $self->set_route;
  }

  $self->set_hostname
    if $self->{hostname} || $self->{fqhostname};

  $self->set_DNS
    if $self->{domain} || $self->{search} || $self->{dns1} || $self->{dns2};

  $self->set_hosts
    if $self->{usehosts} || $self->{hostname} || $self->{fqhostname};

  $self->export_NFS
    if $self->{nfsexports};

  $self->set_NIS_domain
    if $self->{nisdomain};

  $self->switch_files;
  
  # Handle execpost.
  $self->do_execpost if $self->{execpost};
}

sub save_service {
  my $self = shift;
  $self->print_debug("Saving last used service.");

  my $file = $self->{servicefilename};
  open (F, ">$file") || die "Couldn't open \"$file\" [$!]";
  print F "$self->{service}\n";
  close F;
}


###----------------------------------------------------------------###

sub do_command {
  my $self = shift;
  my ($_msg, $_cmd) = @_;
  print $_msg if $self->{verbose};

  $self->print_debug( $_cmd );

  my $return_val = system($_cmd);
  return if $self->{quite_mode};

  ### show a code of what happened
  my $rc = undef;
  if( $return_val != 0 ){
    $self->print_ANSI_color('error');
    printf(" [ Error: %d ]\n", ($rc =$?>>8)) if $self->{verbose};
    $self->print_ANSI_color('normal');
  } else {
    $self->print_ok;
  }
  
  return $rc;
}

###----------------------------------------------------------------###

sub do_execpre {
  my $self = shift;
  $self->do_command("Executing pre-switch script...\n",
                    $self->{execpre});
}

sub do_execpost { 
  my $self = shift;
  $self->do_command("Executing post-switch script...\n",
                    $self->{execpost});
}

###----------------------------------------------------------------###

sub configure_wireless {
  my $self = shift;

  $self->do_command("Setting up ESSID...\n",
                    "$self->{iwconfig} $self->{device} essid $self->{essid}")
    if defined $self->{essid};

  $self->do_command("Setting up iwconfig key...\n",
                    "$self->{iwconfig} $self->{device} key $self->{key}")
    if defined $self->{key};
}

sub configure_device {
  my $self = shift;

  if( $self->{ipaddr} =~ m/dhcp/i ){

    #This line prints to stderr because of Switcher
    print STDERR $self->ANSI_color('normal')."Determining DHCP/BOOTP info...\n"
      if ! $self->{quiet_mode} && $self->{verbose};

    $self->do_command("Configuring device...\n",
                      "$self->{dhcpconfig} $self->{device}");

  }else{

    $self->do_command("Killing dhcp client...\n",
                      "$self->{dhcpkill}");

    my $IFLINE = "$self->{ifconfig} $self->{device} $self->{ipaddr}";
    $IFLINE .= " netmask $self->{netmask}" if defined $self->{netmask};

    $self->do_command("Configuring device...\n",
                      $IFLINE);

  }
}

###----------------------------------------------------------------###

sub delete_route {
  my $self = shift;
  print "Checking for default route...\n" if $self->{verbose};

  my $RUNLINE = "$self->{netstat} -nr | awk '{print \$1;}' | grep '0\.0\.0\.0'";

  $self->print_debug($RUNLINE);

  if( `$RUNLINE` =~ m/0/ ){
    $self->do_command ("Removing default route...\n",
                       "$self->{route} del default");
  }
}


sub set_route {
  my $self = shift;
  $self->do_command("Setting default route...\n",
                    "$self->{route} add default gw $self->{gateway} $self->{device}");
}

###----------------------------------------------------------------###

sub set_hostname {
  my $self = shift;
  my $hostname = $self->{hostname} || $self->{fqhostname};
  $self->do_command("Setting hostname...\n",
                    "$self->{hostnamecmd} $hostname");
}

sub set_DNS {
  my $self = shift;

  my $DNSFILE = $self->{dnsfile};
  print "Setting up DNS (via $DNSFILE)...\n" if $self->{verbose};

  if( $self->{backup_files} && -T $DNSFILE ){
    $self->do_command("Backing up $DNSFILE...\n",
                      "$self->{mv} $DNSFILE $DNSFILE.".time());
  }
  
  if( -T $DNSFILE ){
    $self->do_command("Removing $DNSFILE...\n",
                      "$self->{rm} $DNSFILE");
  }

  print "Writing to $DNSFILE...\n" if $self->{verbose};
  open (F, ">$DNSFILE") || die "Couldn't open $DNSFILE [$!]";
  print F "domain $self->{domain}\n" if $self->{domain};
  print F "search $self->{search}\n" if $self->{search};

  ### do any nameserver entries with key dns1, dns2, dns3, etc
  foreach my $key (keys %$self){
    next unless $key =~ /^dns\d+$/i;
    print F "nameserver $self->{$key}\n";
  }
  close(F);
  $self->print_ok;
}

sub set_hosts {
  my $self = shift;

  my $HOSTSFILE = $self->{hostsfile};
  print "Configuring hosts (via $HOSTSFILE)...\n"
    if $self->{verbose};

  ### backup and remove file if necessary
  if( -T $HOSTSFILE ){
    if( $self->{backup_files} && ! -l $HOSTSFILE ){
      $self->do_command("Backing up $HOSTSFILE...\n",
                        "$self->{mv} $HOSTSFILE $HOSTSFILE.".time());
    }

    $self->do_command("Removing $HOSTSFILE...\n",
                      "$self->{rm} $HOSTSFILE");
  }

  ### write out a clean file
  if( ! $self->{usehosts} ){

    $self->{hostname} ||= '';
    $self->{fqhostname} ||= '';
    print "Writing to $HOSTSFILE...\n" if $self->{verbose};
    open (F, ">$HOSTSFILE") || die "Couldn't open $HOSTSFILE [$!]";
    print F "# Generated by Quickswitch. Do not modify.\n\n";
    print F "127.0.0.1 localhost localhost.localdomain\n";
    print F "$self->{ipaddr} $self->{hostname} $self->{fqhostname}\n"
      if $self->{ipaddr} !~ /dhcp/i;
    close(F);
    $self->print_ok;

  ### or just link the file into place  
  }else{  

    $self->switch_file($self->{usehosts},$HOSTSFILE);

  }
}

sub export_NFS {
  my $self = shift;
  $self->switch_file($self->{nfsexports}, $self->{nfsexportsfile});
  $self->do_command("Exporting filesystems...\n", 
                    $self->{nfsexportall});
}

sub set_NIS_domain {
  my $self = shift;
  $self->do_command("Setting NIS domain...\n",
                    "$self->{niscommand} $self->{nisdomain}");
}

###----------------------------------------------------------------###

sub switch_files {
  my $self = shift;
  while (my($key,$val) = each %{ $self->{switch_files} } ){
    if( $self->{services}->{ $self->{service} }->{$key} ){
      $self->switch_file($self->{services}->{ $self->{service} }->{$key},
                         $val);
    }
  }
}
	
sub switch_file {
  my $self = shift;
  my $linkto   = shift;
  my $linkfrom = shift;

  print "Configuring $linkfrom...\n" if $self->{verbose};

  if( $self->{backup_files} && -e $linkfrom ){
    $self->do_command("Backing up \"$linkfrom\"...\n",
                      "$self->{mv} $linkfrom $linkfrom.".time());
  }
  
  $self->do_command("Symlinking $linkfrom to $linkto...\n",
                    "$self->{ln} -s -f $linkto $linkfrom");
}

###----------------------------------------------------------------###

sub show_usage {
  print "$PROGNAME $VERSION $AUTHOR $DATE\n";
  print "Usage:\nswitchto [-l][-d level][-b][-q][-s][-p] servicename\n";
  print "switchto -a ip:ipaddr [netmask:netmask] [host:hostname] [fqhost:fqhostname]\n";
  print "\t[gw:gateway] [domain:domainname] [dns1:primarydns] [dns2:secondarydns]\n\t[usehosts:file]\n\n";
  print "\t-l: List available services\n";
  print "\t-d: Debug level. Can be 0 (None), 1 (Info), 2 (Debug)\n";
  print "\t-q: Quiet mode. Absolutely no output.\n";
  print "\t-b: Backup all overwritten files.\n";
  print "\t-s: Use SWITCHTO kernel parameter or last used service.\n";
  print "\t-p: Pause Seconds. Can be any integer.  Seconds to pause after operation is done.\n";
  print "\t-h: Help. Display this screen.\n";
  print "\t-v: Version. Display program version.\n";
  print "\t-a: Use command line switching.\n";
  print "\t--nocolors: Disable ANSI colors.\n";
  print "\nCommandline mode:\n\tip: Ip Address (eg. ip:192.168.1.12 or dhcp)\n";
  print "\tnetmask: Subnet mask (if any)\n\tgw: Default gateway\n";
  print "\tdomain: Domain Name\n\tdns1/dns2: Pri / sec DNS address\n";
  print "\thost: Hostname\n\tfqhost: Fully Qualified hostname\n";
  print "\tusehosts: Hosts file name to use.\n"; 
  print "\tessid: Wireless LAN essid.\n"; 
  print "\tkey: Wireless LAN key.\n"; 
  print "\neg., To switch to the profile named 'Home' with debuglevel 2:\n\tswitchto -d 2 Home\n";
  print "\nOr a command line switch:\n";
  print "\tswitchto -a ip:192.168.1.12 netmask:255.255.255.0 gw:192.168.1.51 dns1:192.168.1.51\n";
  exit;
};

###----------------------------------------------------------------###

sub humanerr {
  my $self = shift;
  print @_;
  exit 1;
}

# Ansi colors...

sub print_ANSI_color {
  my $self = shift;
  my $arg = shift;
  print $self->ANSI_color($arg);
}

sub ANSI_color {
  my $self = shift;
  my $ESC = chr(27);
  my $arg = shift;
  $arg = 'normal' unless defined $arg;
  my $code = $self->{"color_$arg"};
  $code = 0 unless defined $code;

  if( $self->{useansi} ){	
    return ($ESC . "[" . $code . "m");
  } else {
    return "";
  }
}

sub print_debug {
  my $self = shift;
  my $line = shift;
  return unless $self->{debug};
  print "=> "
    . $self->ANSI_color('debug')
    . $line
    . $self->ANSI_color('normal')
    . "\n";
}

sub print_ok {
  my $self = shift;
  return unless $self->{verbose};

  $self->print_ANSI_color('ok');
  print " [ OK ]\n";
  $self->print_ANSI_color('normal');
}

### necessary if this file is required elsewhere
1;
