# isPatchInstalled.pl checks if specified MS hotfix (patch) is installed on # specified machine(s). Initial implementation checks hotfixes subkeys in # registry. # # Written 17-sep-03 by fgb (another quick-n-dirty script) # Mod 25-may-2005, fgb: added key4 (windows xp) # Mod 17-aug-2005, fgb: broke out ChkSubkey function from ChkReg, added win 2003 support. require "ctime.pl"; use Net::Ping; use Win32::Registry; ### evil globals: ### $bDebug = 0; $sAppName = "isPatchInstalled"; $sDate = &ctime(time); chop $sDate; $bFound = 0; $hkr; $bListAll = 0; $sLocalMachine = Win32::NodeName; $sMachine = ""; $sMachineList = ""; $sPatch = ""; $sServer = ""; $bSingleMachine = 0; $nTotalMachines = 0; $bVerbose = 1; $sVersion = "1.1"; _main: ParseCmdArgs(); print "$sAppName $sVersion startup at $sDate...\n\n"; if ($sMachineList eq "") { if (! $bSingleMachine) { $sMachine = $sLocalMachine; } if (-1 != &DoCheck()) { ++$nTotalMachines; } } else { open(FPCLIST,$sMachineList) || die "Can't open file $sMachineList: $!\n"; while () { if (!/^\!/) { # skip comments chop $_; s/\s(.*)//; # remove whitespace s/^\\\\//; # remove leading '\\' $sMachine = $_; if (&IsVisibleOnNet()) { if (-1 != &DoCheck()) { ++$nTotalMachines; } } } } close(FPCLIST); } _thatsAllFolks: if ($nTotalMachines == 1) { print "\n$sPatch "; if (!$bFound) { print "not "; } print "installed on $sMachine.\n"; } else { print "\n$sAppName: checked $nTotalMachines machines.\n"; } print "\n$sAppName complete.\n"; ### end of execution ### subroutines here ### sub DoCheck { $sServer = "\\\\" . $sMachine; # used by Connect() print "Checking $sMachine...\n"; if ($bListAll) { print "Listing all installed patches:\n"; } if (&ChkReg()) { $nFixCount++; } return 0; } sub ChkReg { my $sKey1 = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix"; my $sKeyRoot = "SOFTWARE\\Microsoft\\Updates\\"; my $sKey = ""; my @locations = 'DataAccess','Windows 2000','Windows XP','Windows Server 2003'; my $nRet = 1; if (!$HKEY_LOCAL_MACHINE->Connect($sServer,$hkr)) { print "can't connect to $sServer: $!"; return 0; } &ChkSubkey($sKey1); if ($bFound) { goto _skipIt; } foreach (@locations) { $sKey = $sKeyRoot . $_; &ChkSubkey($sKey); if ($bFound) { last; } } _skipIt: $hkr->Close(); return $nRet; } sub ChkSubkey { my $hk; my $skey = $_[0]; my $nRetVal = 1; my @subkeys = (); my $pSubkeys = \@subkeys; # reference (ptr) to subkeys list $#subkeys = 0; # truncate any elements from prior calls if (!$hkr->Open($skey,$hk)) { print "FYI: can't open key $skey\n"; goto _endChkSubkey; } if (!$hk->GetKeys($pSubkeys)) { print "error reading subkeys of $skey\n"; $nRetVal = 0; } $hk->Close(); my $nCount = scalar @subkeys; if (0 == $nCount) { # no subkeys if ($bVerbose) { print "no subkeys under $skey\n"; } } else { # iterate thru each subkey: foreach (@subkeys) { if ($bListAll) { print "$_\n"; next; } else { if (/$sPatch/) { print "$_ installed\n"; $bFound = 1; last; } } } } _endChkSubkey: return $nRetVal; } sub Help { printf("%s version %s by fgb\n",$sAppName,$sVersion); print "Check machines for installed hotfix.\n"; print "\nusage: $sAppName patch [\\\\machine || /list=machines.lis] \n"; print "\nwarning: only performs regex match so a short patch could be a substring of an\n"; print "installed patch with a longer number! (enter the whole number)\n"; } sub IsVisibleOnNet { $p = Net::Ping->new("icmp"); return ($p->ping($sMachine,1)); } sub ParseCmdArgs { if ($bDebug) { $na = $#ARGV+1; print "dbg: entered ParseCmdArgs w $na args...\n";} while ($#ARGV+1) { $_ = shift @ARGV; if ($bDebug) { print "dbg: parsing $_\n"; } if (/^[\/\-]/) { # is arg a switch? $arg = lc(substr($_,1,3)); # parse 1st 3 chars: if ($arg eq "deb") { $bDebug = 1; } if ($arg eq "lis") { $i = index($_,"="); $i++; $sMachineList = sprintf("%s",substr($_,$i)); } if (($arg eq "hel") || (substr($arg,0,1) eq "?")) { &Help(); exit; } } else { # not a switch if (/^[\\]/) { # single machine (UNC) s/\s(.*)//; # remove whitespace s/^\\\\//; # remove leading \\ $sMachine = $_; $bSingleMachine = 1; } else { $sPatch = $_; } } } if ($sPatch eq "") { $bListAll = 1; } if ($bDebug) { $bVerbose = 1; print "dbg: patch=$sPatch sMachine=$sMachine sMachineList=$sMachineList\n"; } } __END__