#!/usr/bin/perl
#
# $Id: apcpwr.pl,v 1.1 2003/05/13 18:23:23 xxxxxx Exp $
#
# control of APC MasterSwitch power outlet contollers
#
# Syntax: apcpwr list
#         apcpwr probe
#         apcpwr <node> (off|on|reboot)
#
# only certain users can run this and is controlled by ...
#
#

#
# don't buffer output
#
$|=1;

#
# only execute from known places
#
undef $ENV{'PATH'};
undef $ENV{'ENV'};

#
# we need POSIX here for getuid
#
use POSIX;

#
# require (source) our config file
#
use File::Basename;
require "/usr/local/etc/apcpwr.conf";
if (! defined $comm{'read'} || ! defined $comm{'write'}) {
   print "ERROR: apcpwr: all community strings not found in config file\n";
   exit 1;
}

$uid = getuid();
$user = getpwuid($uid);

#
# variables
#
$get   = "/usr/local/bin/snmpget -c$comm{'read'} -v1";
$set   = "/usr/local/bin/snmpset -c$comm{'write'} -v1";
$walk  = "/usr/local/bin/snmpwalk -c$comm{'read'} -v1";
# do this for back level compatibility
if (! defined $switchdb) {
  $switchdb = "/var/db/apcpwr.db";
}

die "No snmp utilities found" if ! -f "/usr/local/bin/snmpget";
die "No poweroutletlist file found" if ! -f "$switchdb";

sub usage() {
   print "$0 - control power outlets via snmp\n\n";
   print "Syntax:\n";
   print "   apcpwr [list]\n";
   print "   apcpwr [probe]\n";
   print "   apcpwr [-f] <node> (off|on|reboot)\n\n";
   print "Where:\n";
   print "   -f     - force off or reboot without prompting\n";
   print "   <node> - is a unique string that identifies the outlet\n";

   exit 1;
}

#
# untaint variables
#
sub untaint {
    my @list = @_;
    for (@list) {
        /(.*)/;
        $_ = $1;
    } wantarray ? @list : $list[0];
}

#
# list nodes that are controlled by these switches
# 
sub list {
   chomp(@outlets = `/usr/bin/grep -v ';\$' $switchdb`);

   $last = "";
   foreach (@outlets) {
      ($sw, $swname, $outlet, $oname) = untaint(split(/;/));
      if ($sw ne $last) {
         print "\n$swname ($sw)\n";
         $last = $sw;
         chomp($value = `$get -Oqv $sw $MasterState`);
         $value =~ s/"//g;
         @state = split(/ +/, $value);
      }
      printf "   %s: %-32s %s\n", $outlet, $oname, $state[$outlet-1];
   }
   exit 0;
}

#
# nslookup routine, returns array of name and ip address
#
sub nslookup {
   my $device = shift(@_);
   my $name = $device;
   my $addr = $device;

   if ($device !~ /[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/) {
      open(nspipe, "/usr/sbin/nslookup $device 2>/dev/null|");
         while(<nspipe>) {
            next unless /^Name:/;
            (undef, $name) = split(' ');
            last;
         }
         $_ = <nspipe>;
         if (length($name) > 0 && /^Address:/) {
            (undef, $addr) = split(' ');
         }
      close(nspipe);
   }

   return(($name, $addr));
}

#
# probe defined @switches and save data in $switchdb
# 
sub probe {
   print "apcpwr: probing power switches for outlet names\n";
   foreach $sw (@switches) {
      print "probing $sw...\n";
      ($name, $addr) = nslookup($sw);

      # get outlet names
      chomp(@outlets = `$walk -Onq $sw $OutletNames`);

      # figure out outlet number and the print data
      foreach $outlet (@outlets) {
         $outlet =~ /$OutletNames\.(\d) "(.*)"/;
         $outletNumber = $1;
         $outletName = $2;
         $outletName = "" if $outletName =~ /Outlet \d/;
         $out = sprintf "%s;%s;%d;%s",
            $addr,$name,$outletNumber,$outletName;
         push(@allswitches, $out);
      }
   }

   #
   # write updated database (sorted)
   #
   open(DB, "> $switchdb");
   foreach $port (@allswitches) {
      print DB "$port\n";
   }
   close DB;
   print "apcpwr: $switchdb database written\n";
   exit 0;
}

#
# usage
#
if (! defined $ARGV[0]) {
   usage();
}

#
# parse arguments
#
while (defined $ARGV[0]) {
   $arg = shift;
   $lcarg = lc $arg;
   if ($lcarg eq "list") {
      list();
   }
   elsif ($lcarg eq "probe") {
      probe();
   }
   elsif ($lcarg =~ /^-f/) {
      $noprompt = 1;
   }
   elsif ($lcarg =~ /^-o/) {
      $switchdb = shift;
      $switchdb = untaint($switchdb);
   }
   elsif ($lcarg =~ /^(off|on|reboot)/) {
      $control = $lcarg;
   }
   else {
      $node = untaint($arg);
   }
}

#
# check out permissions from apcpwr.conf file ($allow hash)
#
if ($allow{'global'} =~ /;*$user(;|$)/) {
   $allowglobal = 1;
}

#
# find node we want in switchdb 
#
if (! defined $node || $node eq '') {
   print "No valid node given. Try again\n";
   exit 1;
}
chomp(@outlets = `/usr/bin/grep $node $switchdb`);
if ($#outlets > 1) {
   print "too many nodes meet entered node string '$node' :\n";
   foreach (@outlets) {
      print "   $_\n";
   }
   print "\ntry another node string\n";
   exit 1;
}
($sw, undef, $outlet, $name) = untaint(split(/;/, $outlets[0]));

#
# does user have access to outlet
#
if (defined $allow{$name} && $allow{$name} =~ /\;*$user(;|$)/) {
   $allowport = 1;
}

#
# exit if permissions not set
#
if (! defined $allowglobal && ! defined $allowport) {
   print "User: $user has invalid permissions for port: $name\n";
   exit 1;
}

#
# now do what we have to do
#
if (! defined $noprompt) {
   if ($control =~ /(off|reboot)/) {
      print "Are you sure? [y|N]: ";
      while(! defined $reply) {
         chomp($reply = <STDIN>);
         $lreply = lc $reply;
         if ($lreply =~ /y/) {
            last;
         }
         else {
            exit 0;
         }
      }
   }
}

#
# ok now get out command for SNMP
#
if ($control eq "off") {
   $command = $outletOff;
}
elsif ($control eq "on") {
   $command = $outletOn;
}
elsif ($control eq "reboot") {
   $command = $outletReboot;
}
else {
   die "invalid command: $command";
}
$snmpcmd = "$set -Oqv $sw $OutletCtl.$outlet i $command";

chomp(@value = `$snmpcmd`);
if ($value[0] ne "$command") {
   print "something went wrong\n";
   print "got '$value[0]' when we expected '$command'\n";
   exit 1;
}
print "'apcpwr: command completed sucessfully\n";

exit 0;

