http://qs1969.pair.com?node_id=79340

fingers has asked for the wisdom of the Perl Monks concerning the following question:

I currently have quite alot of cisco switches and routers that I am responsible for maintaining. I got very sick of having to log into a dozen different devices to make the same changes on everyone of them.

I started playing around with perl hoping it would get the job done. I've really never done much programming in the past, aside from very simple shell scripts.

I wrote the following piece of code to simplify my life abit and am happy with how it works. I know there are some obvious security problems with it (currently I run it as root to be able to ping my targets first to verify that they are up) and there are problem several spots where I could have been much more concise.

I am basically looking for some constructive criticism on what I've got and how to improve it. Like I wrote earlier I don't have much programming experience so don't abuse me too bad.

The script takes as its first argument the name of the file that it will feed to the device it connects to. It can take as additional arguments either the name of a file full of IPs(specified with -l as the second argument) or will accept any number of IPs as arguments.

#!/usr/bin/perl -w #********************************************************************* +** #** Written to make making the same configuration changes to a number +** #** of devices less of a pain in the ass. Written with CISCO Routers +** #** and switches in mind it should work with any devices that can be +** #** sent commands via telnet. +** #********************************************************************* +** use strict; my ($script, $i, $telnetpid); unless ( @ARGV >= 2 ) { die "usage: $0 script ip ... " }#Test to verif +y that the re is the minimum number of arguments $script = $ARGV[0]; #Take the first argument as the name of the fi +le to pull commands from shift; #Remove the first argument from the list of ar +guments #Feeding a list of IPs if ( $ARGV[0] eq '-l' ){ #If the new first argument is -l we know to l +ook for a list of IPs if ( $ARGV[1] ){ #Make sure there is a list specified on the c +ommand lin e open(IPLIST, $ARGV[1]) || die "can't open $ARGV[1]\n"; while(<IPLIST>){ feedip($script, $_); } close(IPLIST) } else { die "Need to supply a list of IPs when using $ARGV[0] + \n"; } } #Feeding IPs from the command line else { foreach $i (@ARGV ) { feedip($script, $i); } } #*************************************************************** #** Subroutines below here ** #*************************************************************** #This subroutine is based heavily on code mooched from "Learning Perl" + by Randal l Schwartz and Tom Christainsen sub telnet { use IO::Socket; my ($host, $port, $kidpid, $handle, $line); unless (@_ == 2) { die "usage: $0 host port" } $host = $_[0]; $port = $_[1]; #creates a tcp connection to the specified host and port $handle = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $host, PeerPort => $port) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # split the program into two processes die "can't fork: $!" unless defined($kidpid = fork()); #the if{} block runs only in the parent process if ($kidpid) { #copy the socket to standard output while (defined ($line = <$handle>)) { print STDOUT $line; } kill("TERM", $kidpid); #send SIGTERM to child } #the else{} block runs only in the child process else { # copy standard input to the socket while (defined ($line = <SCRIPT>)){ print $handle $line; } } } sub feedip { my($p); use Net::Ping; $p = new Net::Ping("icmp"); unless (@_ == 2) { die "usage: $0 host port" } if ($_[1] =~ m{\d+\.\d+\.\d+\.\d+}){ #Test to see if we +were given an IP, invalid IPs can still slip through if ($p->ping($_[1])){ #Test to see that host is up $p->close(); open(SCRIPT, $_[0]) || die "Cannot open $scrip +t\n"; # Open the script file #Fork a new process for the telnet subroutine, + wait for it to finish before returning if (!defined($telnetpid = fork())) { die "Cannot fork: $!"; } elsif ($telnetpid == 0) { telnet($_[1], 23); #Options be +ing sent t o sub telnet( IP, Port) exit; } else { waitpid($telnetpid, 0); } close(SCRIPT) || die "Cannot close $script\n"; + #Close th e script file } else{ print "$_[1] does not appear to be responding, + skipping. \n"; $p->close(); } } else { warn "$_[1] did not appear to be an IP address, skippi +ng.\n "; } }
Thanks in advance for any input you can give me.

2001-05-14 Edit by Corion : Changed title to be more descriptive. Added formatting.