#!/usr/bin/perl -wT package power_rpc27; use Exporter; @ISA = ("Exporter"); @EXPORT = qw( rpc27ctrl ); use Socket; use IO::Handle; use lib "@prefix@/lib"; use libdb; # A little perl module to power cycle something attached to an RPC27. # Thats a serially controlled, power controller. # # XXX The little secretkey handshake is coded in here. If this changes # in capture, you have to change it here too. Look for the "pack" # statement below. # # XXX Note that if someone has already tipped to the power controller # in question, this won't work at all. # Turn off line buffering on output $| = 1; # Set for more output. my $debug = 0; # RPC27 Prompt string my $RPC27_PROMPT = "RPC-27>"; my %RPC27_CMD = ("cycle"=>"reboot", "on" =>"on", "off" =>"off"); # Main routine. # usage: rpc27ctrl(cmd, controller, outlet) # cmd = { "cycle" | "on" | "off" } # controller = # outlet = int, 1 <= outlet <= 20 # # Returns 0 on success. Non-zero on failure. # sub rpc27ctrl { my($cmd, $controller, $outlet) = @_; my($TIP, $i, $insync); if ( $outlet < 1 || $outlet > 20 ) { print STDERR "*** Invalid outlet '$outlet': Must be 1-20\n"; return 1; } # # Form the connection to the controller via a "tip" line to the # capture process. Once we have that, we can just talk to the # controller directly. # if (! ($TIP = tipconnect($controller))) { print STDERR "*** Could not form TIP connection to $controller\n"; return 1; } # # Send a couple of newlines to get the command prompt, and then wait # for it to print out the command prompt. This loop is set for a small # number since if it cannot get the prompt quickly, then something has # gone wrong. # $insync = 0; for ($i = 0; $i < 20; $i++) { my $line; if (syswrite($TIP, "\r\n") == 0) { print STDERR "*** Power control sync write failed ($controller/$outlet)\n"; close($TIP); return 1; } if (sysread($TIP, $line, 1024) == 0) { print STDERR "*** Power control sync read failed ($controller/$outlet)\n"; close($TIP); return 1; } if ($debug) { print "Read: $line"; } if ($line =~ /$RPC27_PROMPT/) { if ($debug) { print "Matched prompt '$RPC27_PROMPT'!\n"; } $insync = 1; last; } } if (! $insync) { print STDERR "*** Could not sync with power controller! ". "($controller/$outlet)\n"; close($TIP); return 1; } if ($debug) { print "Sending '$RPC27_CMD{$cmd} $outlet' to $controller\n"; } # Okay, got a prompt. Send it the string: if (syswrite($TIP, "$RPC27_CMD{$cmd} $outlet\r\n") == 0) { print STDERR "*** Power control write failed ($controller/$outlet)\n"; close($TIP); return 1; } close($TIP); return 0; } # # Connect up to the capture process. This should probably be in a library # someplace. # sub tipconnect($) { my($controller) = $_[0]; my($server, $portnum, $keylen, $keydata, $capreturn); my($inetaddr, $paddr, $proto); my(%powerid_row); local *TIP; my $query_result = DBQueryWarn("select * from tiplines where node_id='$controller'"); if ($query_result->numrows < 1) { print STDERR "*** No such tipline: $controller\n"; return 0; } %powerid_row = $query_result->fetchhash(); $server = $powerid_row{'server'}; $portnum = $powerid_row{'portnum'}; $keylen = $powerid_row{'keylen'}; $keydata = $powerid_row{'keydata'}; if ($debug) { print "tipconnect: $server $portnum $keylen $keydata\n"; } # # We have to send over the key. This is a little hokey, since we have # to make it look like the C struct. # my $secretkey = pack("iZ256", $keylen, $keydata); my $capret = pack("i", 0); # # This stuff from the PERLIPC manpage. # if (! ($inetaddr = inet_aton($server))) { print STDERR "*** Cannot map $server to IP address\n"; return 0; } $paddr = sockaddr_in($portnum, $inetaddr); $proto = getprotobyname('tcp'); for ($i = 0; $i < 20; $i++) { if (! socket(TIP, PF_INET, SOCK_STREAM, $proto)) { print STDERR "*** Cannot create socket.\n"; return 0; } if (! connect(TIP, $paddr)) { print STDERR "*** Cannot connect to $controller on $server($portnum)\n"; close(TIP); return 0; } TIP->autoflush(1); if (! syswrite(TIP, $secretkey)) { print STDERR "*** Cannot write to $controller on $server($portnum)\n"; close(TIP); return 0; } if (! sysread(TIP, $capret, length($capret))) { print STDERR "*** Cannot read from $controller on $server($portnum)\n"; close(TIP); return 0; } my $foo = unpack("i", $capret); if ($debug) { print "Capture returned $foo\n"; } if ($foo == 0) { return(*TIP); } if ($i > 5) { printf STDERR "*** WARNING: $controller on $server($portnum) is busy\n". " Waiting a bit before trying again.\n"; } sleep(5); } print STDERR "*** $controller on $server($portnum) was busy for too long\n"; return 0; } 1;