I've modified an expect script to reset passwords for batch accounts into a
ruby script. Our sysadmin has them expire every month, so we change them to
a temporary value, then. It works and looks neat, but it is slow. I was
hoping that you might have some tips on speeding it up. I'm pretty sure that
I'm not using net/telnet to it's fullest.

Usage is simple, chgpass.rb [test|prod], the flag indicates where we're
changing (frodo being test system, gandalf being production).

Thanks,

Mike Henderson

The Constitution of the United States of America. * 1791. All rights
reserved.


//------------------------------------------------------------------	

#! /usr/bin/env ruby
#####################################################################
#
# To maintain, search for arrUsers.append. Update the list of append
# statements to contain the host, user, password you're working with
#

require 'net/telnet'


#####################################################################
#
#
#
class UserAccount

   def initialize(host, user, pass)
      @host    = host
      @user    = user
      @pass    = pass
      @tn      = nil

      puts
      puts " info: #{__LINE__} #{@host} #{@user} #{@pass}"
      puts

      #
      # can change if you're testing and don't wish to really do this
      #
      @cmdPassword = 'passwd'

   end

   def Close
      @tn.close if @tn
      puts
      puts "logged off"
      puts
   end

   def Login
      #
      # attempt to connect to the host
      #
      begin
         @tn = Net::Telnet.new(
               {'Host'      => @host,
                'Waittime'  => 0,
                'Timeout'   => 15,
                'Debug_Log' => true}
            ) { |str| print str }

         @tn.login(@user, @pass) { |str| print str }

      rescue
         raise
      end

   end

   def ChangePassword(oldPassWord, newPassWord)
      #
      #/userhome/mdhender/src/mdh/spect
      # mdhender@zinc > passwd
      #
      @tn.puts @cmdPassword

      #
      #ksh: passwd:  not found
      #
      begin
         @tn.waitfor("Match" => /not found|denied/, "Timeout" => 3) { |str|
print str }
         puts
         puts "error: #{__LINE__}: invalid command '#{@cmdPassword}' on host
'#{@host}"
         puts
         return false
      rescue TimeoutError
         #
         # wait did not find what it wanted to find. that is good in this
case
         #
      rescue
         puts
         puts "error: #{__LINE__}: Unknown Error"
         puts
         raise
      end

      #
      #/bin/passwd: Permission denied
      #
      begin
         @tn.waitfor("Match" => /Permission denied/, "Timeout" => 3) { |str|
print str }
         puts
         puts "error: #{__LINE__}: invalid command '#{@cmdPassword}' on host
'#{@host}"
         puts
         return false
      rescue TimeoutError
         #
         # wait did not find what it wanted to find. that is good in this
case
         #
      rescue
         puts
         puts "error: #{__LINE__}: Unknown Error"
         puts
         raise
      end

      #
      #passwd:  Changing password for mdhender
      #Enter login password:
      #
      @tn.puts oldPassWord

      #
      #passwd(SYSTEM): Sorry, wrong passwd
      #Permission denied
      #
      begin
         @tn.waitfor("Match" => /SYSTEM/, "Timeout" => 3) { |str| print str
}
         puts
         puts "error: #{__LINE__}: invalid password"
         puts
         return false
      rescue TimeoutError
         #
         # wait did not find what it wanted to find. that is good in this
case
         #
      rescue
         puts
         puts "error: #{__LINE__}: Unknown Error"
         puts
         raise
      end

      #
      #New password:
      #
      @tn.puts newPassWord

      #
      #Re-enter new password:
      #
      @tn.waitfor(/password:/) { |str| print str }
      @tn.puts newPassWord

      #
      #passwd(SYSTEM): They don't match; try again.
      #passwd(SYSTEM): Password too short - must be at least 6 characters.
      #passwd(SYSTEM): Too many failures - try later.
      #New password:
      #

      #
      #passwd (SYSTEM): passwd successfully changed for mdhender
      #
      @tn.waitfor(/passwd successfully changed/) { |str| print str }
      return true

      #
      #/userhome/mdhender/src/mdh/spect
      # mdhender@zinc >
      #
   end

   def Reset

      begin
         self.Login
      rescue TimeoutError
         #
         # unable to log in
         #
         return " failed: #{@user}@#{@host}     -- invalid password
#{@pass}"
      rescue
         puts
         puts "error: #{__LINE__}: Unknown Error"
         puts
         raise
      end

      #
      # use a temporary (but known) password for the update
      #
      if !ChangePassword(@pass, 'tmpWrd12')
         puts
         puts "error: #{__LINE__}: unable to baboozle password for
#{@user}@#{@host}"
         puts

         return " failed: #{@user}@#{@host}"

      else
         if !ChangePassword('tmpWrd12', @pass)
            puts
            puts "error: #{__LINE__}: unable to reset password for
#{@user}@#{@host}"
            puts "error: #{__LINE__}: #{@user}@#{@host} password is
'tmpWrd12' and is corrupted"
            puts
            return " FAILED: #{@user}@#{@host}     -- corrupted -- unix01"
         end

         return "succeed: #{@user}@#{@host}"

      end

   end
end

#####################################################################
#
# create an array to hold the list of user account information
#
class UserList
   def initialize
      @users = Array.new
      @results = Array.new
   end

   def append(host, user, password)
      @users.push([ host, user, password ])
   end

   def [](key)
      return @users[key] if key.kind_of?(Integer)
      return @users.find { |aUser| aUser.name = key }
   end

   def each
      @users.each { |anEntry|
         host = anEntry[0]
         user = anEntry[1]
         pass = anEntry[2]
         yield host, user, pass
      }
   end

   def sort
      @users.sort!
   end

   def push(str)
      @results.push(str)
   end

   def Results
      puts
      puts @results
      puts
   end

end
arrUsers = UserList.new


#####################################################################
#
# specify the accounts to work with
#
# - format is    host_______  user__________ password______
#
arrUsers.append( 'frodo',     'hobbit',      'daRing@' )
arrUsers.append( 'gandalf',   'greyone',     'eag1e'   )


#####################################################################
#
# sort the users array to make output look nicer
#
arrUsers.sort


#####################################################################
#
# attempt to reset the password for each user in the array
#
jArgs = ARGV.dup
if jArgs
   while thisArg = jArgs.shift do
      if thisArg == "test"

         arrUsers.each { |host, user, pass|
         
            begin
               if host =~ /frodo|gandalf/
                  acct = UserAccount.new(host, user, pass)
                  arrUsers.push(acct.Reset)
               end
            ensure
               if acct
                  acct.Close
               end
            end
         }
         arrUsers.Results

      end

      if thisArg == "prod"

         arrUsers.each { |host, user, pass|
         
            begin
               if host =~ /frodo|gandalf/
                  acct = UserAccount.new(host, user, pass)
                  arrUsers.push(acct.Reset)
               end
            ensure
               if acct
                  acct.Close
               end
            end
         }
         arrUsers.Results

      end
   end
end