COLUMN=prnt
LOGFILE=/tmp/$COLUMN.log1
#Filter out some totals
echo "Printers
- Each Office "
for S in `$BB localhost "hobbitdboard test=$COLUMN" | sed 's/|.*//'`; do
s=`echo $S | tr a-z A-Z`
echo "| $s"
done
#NOTE: If the length of this isn't enough to warrant stand-alone pages
#NOTE: then remove the links to prnt-{combined|ports|jobs}.html and prnt-names.txt here
#NOTE: and don't bother using prntx.sh.
echo "
- Combined | (stand-alone)
- Print Jobs | (stand-alone)
- Ports | (stand-alone)
- | Names
Each Office
"
#The output of this is essentially identical to what one would get if `cat`-ing the $BBLOGS if
# "hobbitd_filestore --status" was launched.
for S in `$BB localhost "hobbitdboard test=$COLUMN" | sed 's/|.*//'`; do $BB localhost "hobbitdlog $S.$COLUMN" | tail -n +2; echo ""; done > $LOGFILE
#Get each list of printers, including first line of status
sed '
/Printers list/,/\/table/!d
/Printers list/s/\[\(.*\)\]/[\1<\/a>]/
s/^green/green/
s/^yellow/
yellow/
s/^red/
red/
s/&clear//g
s/&green//g
s/^&yellow/
/g
s/&yellow//g
s/^&red/
/g
s/&red//g
#s/table border=1/table border=1 class=sortable/
s/
MAC/ | MAC/
s/ | Port/ | Port/
' $LOGFILE
#Calculate some statistics
S=`awk ' /Printers list/ {
shares += substr($9,2);
MACs += $12;
unassigned+=$14;
ports+=$17;
jobs+=$20
} END {
print "Totals: " shares " shares on " MACs " MACs, " unassigned " unassigned MACS; " ports " ports; " jobs " jobs"
}' $LOGFILE`
#Get each list of printers, but rather than listing each separately, combine into one table
#S=`sed '/Printers list/,/\/table/!d;/ | /!d' $LOGFILE | wc -l`
echo "
Combined
`echo $S | sed 's/\; [0-9]* jobs//'`
Share | Port | Res | Driver | Version | Processor | Capabilities | Status | Error | Ping | Comment"
sed '
/Printers list/,/\/table/!d
/ | /!d
s/&clear//g
s/&green//g
s/&yellow//g
s/&red//g
' $LOGFILE
echo " |
---|
"
#Get each list of print jobs, but rather than listing each separately, combine into one table
echo "
Print Jobs
`echo $S | sed 's/.*ports./Total:/'`
Status | Owner/ Notify | Total Pages | Size | TimeSubmitted | Document | Description (Printer,Job) | Host PrintQueue | Ping | Notes"
sed '
s/... DATA TRUNCATED .../... DATA TRUNCATED ...\
<\/table>/
/TimeSubmitted/,/\/table/!d
#Put a non-breaking space in the date/time stamp
s/\([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\) \([0-9][0-9][0-9][0-9][0-9][0-9]\)-[0-9][0-9][0-9]/\1\ \;\2/
/ | /!d
s/&green//g
s/&yellow//g
s/&red//g
s/&purple//g
s/&clear//g
s/ UNKNOWN/Unknown/
' $LOGFILE
echo " |
---|
"
#More statistics (for ports list)
S=`sed '
s/... DATA TRUNCATED .../... DATA TRUNCATED ...\
<\/table>/
/Port.LPR\,/,/\/table/!d
/ | /!d
' $LOGFILE | wc -l`
#Get each list of ports
echo "
Ports
Total: `echo $S` ports
Reservation | Reservation Description | Address | Ping | MAC | TCP, Port|LPR, Name | SNMP | Driver"
sed '
s/... DATA TRUNCATED .../... DATA TRUNCATED ...\
<\/table>/
/Port.LPR\,/,/\/table/!d
/ | /!d
s/&green//g
s/&yellow//g
s/&red//
s/public, 1/public\/1/
' $LOGFILE
echo " |
---|
"
echo "As of `date` "
echo ""
== prntx.sh ==
Customize as you like; at this company, we use a convention of //city//-//department//# with an optional -PS or -PCL, yours might be different and the prnt-names.txt counts by department might not be useful/desirable.
echo "Printers combined
Combined printers
`sed '/name=combined/,/name=jobs/!d;/a name=/d;/\ /d' < $BBHOME/www/prnt.html`
" > $BBHOME/www/prnt-combined.html
echo "Print jobs
Print jobs
`sed '/name=jobs/,/name=ports/!d;/a name=/d;/\ /d' < $BBHOME/www/prnt.html`
" > $BBHOME/www/prnt-jobs.html
echo "Printer ports
Printer ports
`sed '/name=ports/,/^\As of /!d;/a name=/d;/\ /d' < $BBHOME/www/prnt.html`
" > $BBHOME/www/prnt-ports.html
echo "Counts of printer names (regardless of site)" > $BBHOME/www/prnt-names.txt
sed '1,6d;$d;s/^.tr..td.//;s/.*//' $BBHOME/www/prnt-combined.html | tr A-Z a-z | sed '$d;/-ps$/d;/-pcl/d;/-universal/d;s/^[^-]*-//;s/-.*//' | sort | uniq -c >> $BBHOME/www/prnt-names.txt
echo "
Counts of printer base names" >> $BBHOME/www/prnt-names.txt
sed '1,6d;$d;s/^.tr..td.//;s/ | .*//' $BBHOME/www/prnt-combined.html | tr A-Z a-z | sed '$d;/-ps$/d;/-pcl/d;/-universal/d;s/^[^-]*-//;s/-.*//;s/[0-9].*//' | sort | uniq -c >> $BBHOME/www/prnt-names.txt
echo "
Notes:
* This excludes the following duplicate entries:
*-PS names (e.g. 5 named COPIER1-PS would give 14 instead of 9 COPIER1)
*-PCL* (usually PCL5 or PCL6, mostly at PDX)
*-Universal (a duplicate using the HP Universal driver, all in ONT)
" >> $BBHOME/www/prnt-names.txt
===== Source =====
==== listprn.vbs ====
Option Explicit
'List printers (for Xymon/Hobbit/Big Brother), by goldfndr
'Note: This script assumes that one uses Share names identical to Printer names. (WMIC Printer GET Name,Sharename)
'(If printer names have spaces, they will defy pinging, and the print jobs ping column will be less useful.)
'FIXME: Determine what voodoo allows LOCALSYSTEM to DHCP dump remotely (netsh.exe -c dhcp -r dhcpserv server dump)
Dim TimerStart : TimerStart = Timer
Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WSH : Set WSH = CreateObject("WScript.Shell")
Dim WMI, wmiq, p : Set WMI = GetObject("winmgmts:\\.\root\cimv2")
'Dim me : me = WSH.Environment("Process")("COMPUTERNAME")
const ssfSYSTEM = 37
Dim SHA : Set SHA = CreateObject("Shell.Application")
Const dhcplog = "dhcp.txt" 'Not normally deleted, left for debugging purposes
const dbg=true
Dim subcolor, color : color = "green"
Const td = "" 'change to vbTab as needed, for inter-field separator
Const th = " | " 'change to vbTab as needed, for inter-field separator
Const D = " " 'Change to "; " as needed, for intra-field delimiter/separator (notes)
Const IPpattern = "\.205\.|\.1\d\.2\d\d$" 'A regexp valid for your printers' IP addrs, for DHCP
Const MYDOMAIN = ".example.com"
Const DHCPDescriptions = True
'***********************************************************
Phase "Determine DHCP server"
'Note: This check assumes that the DHCP server is in the DNS search path.
'Note: Duplication will result if there are multiple print servers within one DHCP subnet.
'Note: The user that this script runs under needs permissions to do remote netsh checks.
'Note: DNS Search Order should be either null (no DNS) or an array; null might be from a virtual adapter.
Dim dhcpserver : dhcpserver = ""
Set wmiq = WMIQuery("IPAddress, DHCPServer, DNSServerSearchOrder" _
& "; Win32_NetworkAdapterConfiguration Where IPEnabled = True")
'***********************************************************
Dim t, f : For each p in wmiq
For each t in p.IPAddress
wscript.echo "IP Address=" & t
'if t = p.DHCPServer then dhcpserver = "" : exit for
'wscript.echo "VarType=" & VarType(p.DNSServerSearchOrder)
If IsArray(p.DNSServerSearchOrder) then
For each f in p.DNSServerSearchOrder
wscript.echo "DNS Server=" & f
if t = f then 'If this print server is a DNS server
dhcpserver = "" 'then query is local, not remote
exit for
end if
if "" = dhcpserver then dhcpserver = f
Next
End If
Next
Next
wscript.echo "Determined DHCP server=""" & dhcpserver & """"
'wscript.quit
'***********************************************************
Phase "Dump DHCP " & dhcpserver
t = "netsh.exe -c dhcp server dump"
if "" <> dhcpserver then t = Replace(t, "-c dhcp", "-c dhcp -r " & dhcpserver) 'FIXME: Permissions?
WSH.Run "cmd.exe /c " & t & " 2>" & dhcplog & ".err | find ""reservedip"" | sort > " & dhcplog, 0, 1
Phase "Parse DHCP, gathering Ports (IP Addresses), MAC Addresses, and Reservations (Reserved Names)"
'***********************************************************
Dim ports : Set ports = CreateObject("Scripting.Dictionary")
Dim macs : Set macs = CreateObject("Scripting.Dictionary")
Dim reservations : Set reservations = CreateObject("Scripting.Dictionary")
Dim resdescriptions : Set resdescriptions = CreateObject("Scripting.Dictionary")
Dim tcpips : Set tcpips = CreateObject("Scripting.Dictionary")
Dim pingables : Set pingables = CreateObject("Scripting.Dictionary")
if FSO.GetFile(dhcplog).Size Then
Set f = FSO.OpenTextFile(dhcplog, 1)
Do
Dim s : s = f.ReadLine
t = Split(s)
ports.Item(t(7)) = t(7)
macs.Item(t(7)) = t(8)
dim reservation : reservation = Mid(t(9), 2, len(t(9)) - 2)
reservations.Item(t(7)) = reservation
'strip off MYDOMAIN if present, case-insensitive search starting at 4th character
if Instr(4, reservation, MYDOMAIN, 1) then
reservations.Item(t(7)) = left(reservation, len(reservation) - len(MYDOMAIN))
end if
resdescriptions.Item(t(7)) = Split(s, """ """)(1)
Loop until f.AtEndOfStream
f.Close
End If
Dim ErrorStateTxt : ErrorStateTxt = Array("", "Other", "No Error", "Low Paper", "No Paper" _
, "Low Toner", "No Toner", "Door Open", "Jammed", "Offline", "Service Requested", "Output Bin Full")
Dim ErrorStateVal : ErrorStateVal = Array(0, 1, 0, 1, 2, 1, 2, 2, 2, 2, 2, 2) '0=green, 1=yellow, 2=red
Dim PrinterStatusTxt : PrinterStatusTxt = Array("", "Other", "Unknown", "Idle", "Printing" _
, "Warming Up", "Stopped printing", "Offline")
Dim colors : colors = Array("green", "yellow", "red")
Dim exps : Set exps = CreateObject("Scripting.Dictionary") 'Expansion for port
Dim drivers : Set drivers = CreateObject("Scripting.Dictionary")
Dim processors : Set processors = CreateObject("Scripting.Dictionary")
Dim datatypes : Set datatypes = CreateObject("Scripting.Dictionary")
Dim totals(3) : totals(0) = 0 : totals(1) = 0 : totals(2) = 0 : totals(3) = 0
Dim versions : Set versions = CreateObject("Scripting.Dictionary")
Dim LINE : LINE = ""
'***********************************************************
Phase "Evaluate the TCP/IP Ports"
WMI.Security_.Privileges.AddAsString "SeLoadDriverPrivilege" 'Required for SNMP/etc.
Set wmiq = WMIQuery("Win32_TCPIPPrinterPort")
'***********************************************************
For Each p in wmiq
t = " | " & td & reservations(p.HostAddress) & td & resdescriptions(p.HostAddress) _
& td & p.HostAddress & td & "&" & pingable(p.HostAddress) _
& td & MACs(p.HostAddress) & td
Select Case p.Protocol
Case 1: t = t & "Raw, "
if p.PortNumber <> 9100 then _
t = t & "" & p.PortNumber & "" else t = t & "9100"
Case 2: t = t & "LPR, " & p.Queue
if p.ByteCount then t = t & " (Counted)"
Case Else: t = t & "Unknown (" & p.Protocol & ")"
End Select
t = t & td
if p.SNMPEnabled then t = t & p.SNMPCommunity & ", " & p.SNMPDevIndex
wscript.echo t
tcpips(p.HostAddress) = t
next
Phase "Determine ""Product Version"" and ""File Version"" column indexes"
dim productversion, fileversion
Set f = SHA.NameSpace(ssfSYSTEM)
For t = -1 to 50
if "product version" = LCase(f.GetDetailsOf(Nothing, t)) then productversion = t
if "file version" = LCase(f.GetDetailsOf(Nothing, t)) then fileversion = t
Next
Function DriverVersion(p)
'The logic in here is from analysis of HP and Canon drivers. YMMV.
wscript.echo "Checking " & p.DriverPath
Dim f, v : f = FSO.GetFileName(p.DriverPath)
if UCase(f) <> "UNIDRV.DLL" and UCase(f) <> "PSCRIPT5.DLL" then
v = ProductVer(p.DriverPath)
'FIXME: Add version checking for Oce drivers that say "see ocewpdid.dll" (really!)
else
f = FSO.GetFileName(p.ConfigFile)
if UCase(f) <> "UNIDRVUI.DLL" and UCase(f) <> "PS5UI.DLL" then
v = ProductVer(p.ConfigFile)
if len(v) >= 3 then v = v & " (" & Replace(LCase(f), ".dll", "") & ")"
end if
'Sometimes the ProductVer is unavailable, e.g. hpzpi4wm.dll@2007-02-13
if UCase(f) = "UNIDRVUI.DLL" or UCase(f) = "PS5UI.DLL" or Len(v) < 3 then
wscript.echo "Checking dependent files" & vbTab & p.Name & " " & p.SupportedPlatform
v = DependentFilesVersion(p.DependentFiles)
if len(v) < 3 then
wscript.echo "Still no version, getting date of " & p.DataFile
f = FSO.GetFile(p.DataFile).DateLastModified
v = "[" & LCase(FSO.GetExtensionName(p.DataFile)) & "~" _
& Year(f) & "-" & Right(0 & Month(f), 2) & "-" & Right(0 & Day(f), 2) & "]"
end if
end if
end if
f = Left(p.Name, Len(p.Name) - 2 - Len(p.SupportedPlatform) - Len(p.Version))
'wscript.echo vbTab & v & vbTab & """" & f & """"
DriverVersion = Array(v, f)
'wscript.echo f & vbTab & versions(f)
End Function
Private Function DependentFilesVersion(df) 'Pass the array of dependent files; only called by DriverVersion
'Prefer x*ui*.dll but settle for *ui*.dll, or *.dll; but at least capture the first filename
Dim a, fn : a = Filter(df, ".dll", True, vbTextCompare)
if UBound(a) >= 0 then
fn = a(0)
a = Filter(a, "ui", True, vbTextCompare)
if UBound(a) >= 0 then
fn = a(0)
a = Filter(a, "\x", True, vbTextCompare)
if UBound(a) >= 0 then
fn = a(0)
end if
end if
else
DependentFilesVersion = ""
Exit Function
end if
wscript.echo "Checking dependent file " & fn
Dim v : v = ProductVer(fn)
if Instr(v, ",") > 0 then v = FileVer(fn) 'if commas in Product Version then use File Version
if len(v) >= 3 then v = v & " [" & LCase(FSO.GetBaseName(fn)) & "]" 'credit/blame
DependentFilesVersion = v
End Function
Function ProductVer(filename)
Dim nmsp : Set nmsp = SHA.Namespace(FSO.GetParentFolderName(FSO.GetAbsolutePathName(filename)))
Productver = nmsp.GetDetailsOf(nmsp.ParseName(FSO.GetFileName(filename)), productversion)
End Function
Function FileVer(filename)
Dim nmsp : Set nmsp = SHA.Namespace(FSO.GetParentFolderName(FSO.GetAbsolutePathName(filename)))
Filever = nmsp.GetDetailsOf(nmsp.ParseName(FSO.GetFileName(filename)), fileversion)
End Function
'***********************************************************
Set wmiq = WMIQuery("Name, ConfigFile, DataFile, DriverPath, DependentFiles, SupportedPlatform, Version" _
& "; Win32_PrinterDriver")
Phase "Enumerate Printer drivers and determine their product versions"
'***********************************************************
For Each p in wmiq
dim a : a = DriverVersion(p) : f = a(1)
if versions.exists(f) then
versions(f) = versions(f) & " "
else
versions(f) = ""
end if
versions(f) = versions(f) & Mid(p.SupportedPlatform, InStrRev(p.SupportedPlatform, " ") + 1) & "=" & a(0)
Next
Function DontShout(drivername) 'These companies like to SHOUT their names, but their websites use lcase. Rude!
DontShout = drivername
Dim s : for each s in Split("Konica Minolta Ricoh Sharp")
DontShout = Replace(DontShout, UCase(s), s)
next
End Function
Function ShareStatus(ps, s)
ShareStatus = PrinterStatusTxt(ps) & Replace(", " & s, ", Unknown", "") 'Don't list Status if Unknown
ShareStatus = Replace(ShareStatus, "Other, ", "=>") 'Don't list PrinterStatus if Other
if "<" = Left(td, 1) then ShareStatus = Replace(ShareStatus, "=>", "=>") 'Use HTML if needed
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'The WMI Query line is where servers sometimes get stuck.
'It is similar to the command: wmic printer list brief
'One could check WMI goodness with: wmic server
'Note: restarting Print Spooler service doesn't unstuck.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'***********************************************************
Set wmiq = WMIQuery("Win32_Printer")
Phase "Enumerate expansions for each known printer, and add its driver/processor/datatype by sharename"
'***********************************************************
For each p in wmiq
Phase "Check printer port " & p.PortName
totals(0) = totals(0) + 1
'WScript.Echo p.DeviceID & vbTab & " MarkingTechnology: " & p.MarkingTechnology
'if len(p.ShareName) then
dim c : c = ""
if not IsNull(p.CapabilityDescriptions) then
'Collate: True if a printer has collating bins.
'Color: True if a printer can print in color. (but see LaserJet note below)
'Duplex: Indicates the type of duplex support a printer has.
'(HP claims Color on 4M, 4 Plus, 5Si MX, 8000, 8100, 8150, 9050 as of Server 2003.)
'(Oce clams Color on TDS400, TDS450, TDS600, TDS700 as of Server 2003.)
'(Try it yourself: WMIC Printer GET CapabilityDescriptions,DriverName)
'FIXME: add honesty override for manufacturers setting this on non-color devices?
Phase "Check port " & p.PortName & " capabilities"
for each t in p.CapabilityDescriptions
'Never seen one that lacked "Copies", so filter that out
if t <> "Copies" then c = c & t & " "
next
c = Replace(Trim(c), " ", "|")
end if
dim port : port = p.PortName
dim mac : mac = ""
if "IP_" = Left(p.PortName, 3) then
port = Mid(p.PortName, 4) 'Strip off the "IP_" if present
if not ports.Exists(port) then ports(port) = port
if macs.Exists(port) then
mac = macs(port)
' macs.Remove(port) 'Possibly reused?
' reservations.Remove(port)
end if
end if
dim ping : ping = pingable(port)
Phase "Check printer's error state, complete expansion"
subcolor = colors(ErrorStateVal(p.DetectedErrorState))
dim res : res = p.HorizontalResolution
if res <> p.VerticalResolution then res = res & "x" & p.VerticalResolution
Dim errtxt : errtxt = ErrorStateTxt(p.DetectedErrorState)
exps(port) = "" _
& td & p.ShareName _
& td & port _
& td & res _
& td & DontShout(p.DriverName) & td & versions(p.DriverName) _
& td & p.PrintProcessor _
& td & c _
& td & ShareStatus(p.PrinterStatus, p.Status) _
& td & "&" & subcolor & " " & errtxt _
& td & "&" & ping _
& td & p.Comment _
& Replace(td, "<", "") & vbCRLF & exps(port)
if dbg then wscript.echo port & vbTab & exps(port)
if "green" = ping then
if 9 = p.DetectedErrorState then 'Offline
if p.Status = "Degraded" and 1 = p.PrinterStatus then 'Other
LINE = LINE & "&red " & p.ShareName & " has Other/Degraded/Offline" _
& ", hallmarks of Spooler service restart required." & vbCRLF
color = "red"
else
LINE = LINE & "&red " & p.ShareName & " can be pinged at " & port _
& " but claims " & errtxt & "; restart Spooler service?" & vbCRLF
if color <> "red" then color = "yellow"
end if
end if
end if
Phase "Add share " & p.ShareName & " to drivers list"
drivers(p.ShareName) = p.DriverName
Phase "Add share " & p.ShareName & " to processors list"
processors(p.ShareName) = p.PrintProcessor
Phase "Add share " & p.ShareName & " to datatypes list"
datatypes(p.ShareName) = p.PrintJobDataType
Phase "Finished with Adding share " & p.ShareName & " to lists"
'end if
Next
'Oddly, if Win32_Printer isn't requeried, "SWbemObjectSet: Unspecified error"
Set wmiq = WMIQuery("Win32_Printer")
Phase "Remove MACs with corresponding printers from DHCP reservation list"
'WARNING: Script can sometimes hang right at this next line; next Phase line twice for debugging
For Each p in wmiq
Phase "Check printer MAC"
Phase "Check printer MAC " & p.PortName
'if len(p.ShareName) then
port = Mid(p.PortName, 4) 'Strip off the "IP_"
if macs.Exists(port) then
Phase "Check printer MAC for " & port
totals(2) = totals(2) + 1
macs.Remove(port)
reservations.Remove(port)
end if
'end if
Next
'***********************************************************
Phase "List each printer share with its expansion"
'***********************************************************
if len(LINE) > 1 then LINE = LINE & vbCRLF
LINE = LINE & "" & vbCRLF _
& "" &th& "Share" &th& "Port" &th& "Res" _
&th& "Driver" &th& "Version" &th& "Processor" &th& "Capabilities"
'if Instr(th, "<") > 0 then LINE = LINE & "" else LINE = LINE & th
LINE = LINE & th
LINE = LINE & "Status" & th & "Error" & th & "Ping" & th & "Comment" & vbCRLF
for each p in ports.Keys
'wscript.echo vbTab & p & vbTab & exps(p)
if len(exps(p)) then LINE = LINE & exps(p)
next
LINE = LINE & vbCRLF & " | " & vbCRLF
'Some credit to http://blogs.msdn.com/b/ericlippert/archive/2004/12/03/274360.aspx
Function CULng(ByVal x)
If x < 0 Then CULng = x + 2^32 Else CULng = x
End Function
'***********************************************************
Dim jobs : jobs = ""
Set wmiq = WMIQuery("Win32_PrintJob")
Phase "Enumerate Print jobs"
'***********************************************************
For Each p in wmiq
totals(3) = totals(3) + 1
Dim eltime : eltime = ""
if not IsNull(p.ElapsedTime) then
eltime = CLng(Left(p.ElapsedTime,14))
if 0 = eltime then eltime=""
end if
Select case p.Status
Case "Error": subcolor = "yellow" : if color <> "red" then color = "yellow"
LINE = "&yellow """ & p.Document & """ at " & p.Description & " has error." _
& vbCRLF & LINE
Case "UNKNOWN": subcolor = "clear"
Case "Degraded":subcolor = "yellow" 'Usually paused jobs so don't propagate
Case "OK": subcolor = "green"
Case Else: subcolor = "clear" : if color = "green" then color = "clear"
'Pred Fail,Starting,Stopping,Service,Stressed,NonRecover,No Contact,Lost Comm
End Select
jobs = jobs & " " & td & "&" & subcolor & " " & p.Status
if p.Owner <> p.Notify then
jobs = jobs &td& p.Owner &"/"& p.Notify
else
jobs = jobs &td& p.Owner
end if
'Some print jobs are over 2 GB, so make sure the size is an Unsigned Long.
jobs = jobs &td& p.TotalPages &td& CULng(p.Size) &td& dt(p.TimeSubmitted) _
&td& p.Document &td& p.Description &td& p.HostPrintQueue
Dim k, pinged : pinged = "&clear" : for each k in exps.keys
If Instr(exps(k), td & Split(p.Description, ",")(0) & td) > 0 then
'wscript.echo p.Document & vbTab & k & vbTab
pinged = "&" & pingable(k)
End If
next
'Expansions won't be found if the printer name differs from the share name.
if pinged = "&clear" then Phase "Unable to find expansion for " & p.Description
jobs = jobs & td & pinged & " &" & pingable(Replace(p.HostPrintQueue, "\\", ""))
Dim notes : notes = ""
if p.PagesPrinted > 0 then notes = notes & D & "PagesPrinted=" & p.PagesPrinted
if len(p.StartTime) > 0 then notes = notes & D & "StartTime=" & p.StartTime
if len(eltime) > 0 then notes = notes & D & "ElapsedTime=" & eltime
if len(p.UntilTime) <> 0 then notes = notes & D & "UntilTime=" & p.UntilTime
if len(p.Parameters) > 0 then notes = notes & D & "Parameters=" & p.Parameters
if p.Priority <> 1 then notes = notes & D & "Priority=" & p.Priority
if len(p.JobStatus) > 0 then notes = notes & D & "JobStatus=""" & p.JobStatus & """"
if p.StatusMask <> 0 then notes = notes & D & "StatusMask=" & p.StatusMask
if len(p.InstallDate) <> 0 then notes = notes & D & "InstallDate=" & p.InstallDate
'We have the expected processor/driver/datatype by share, so note it if it doesn't match
dim sn : sn = left(p.Description, Instr(p.Description, ",") - 1) 'Share Name
if p.PrintProcessor <> processors(sn) then notes = notes & D & "PrintProcessor=""" & p.PrintProcessor & """"
if p.DriverName <> drivers(sn) then notes = notes & D & "Driver=""" & p.DriverName & """"
if p.DataType <> datatypes(sn) then notes = notes & D & "DataType=""" & p.DataType & """"
if len(notes) > 0 then notes = Mid(notes, 1+len(D)) 'Remove initial delimiter
jobs = jobs &td& notes & vbCRLF
Next
'***********************************************************
Phase "If there are any print jobs then add them to the report"
'***********************************************************
if len(jobs) > 0 then LINE = LINE & "
" & vbCRLF _
& "" & th & "Status" & th & "Owner/ Notify" _
& th & "Total Pages" & th & "Size" & th & "TimeSubmitted" _
& th & "Document" & th & "Description (Printer, Job)" & th & "Host PrintQueue" _
& th & "Ping" & th & "Notes" & vbCRLF & jobs & " "
'***********************************************************
Phase "Expand MACs without corresponding printers (e.g. digital senders) as unused"
'***********************************************************
Dim r : set r = New RegExp
r.Pattern = IPpattern
for each p in macs.Keys
'set c = r.Execute(p) : wscript.echo vbTab & c.Count & vbTab & p
if r.Execute(p).Count > 0 then
'totals(1) = totals(1) + 1
'exps(p) = "" & td & reservations(p) & td & resdescriptions(p) & td & macs(p) & td & p & td _
' & td & "(unused at " & WSH.Environment("Process")("COMPUTERNAME") & ") " _
' & td & td & td & td & td & td & td & "&" & pingable(p) & " " & td & vbCRLF & exps(p)
if 0 = len(tcpips(p)) then
tcpips(p) = " " & td & reservations(p) & td & resdescriptions(p) _
& td & p & td & "&" & pingable(p) & td & macs(p) & td _
& divright("unused") & td
end if
else
if ports.Exists(p) then ports.Remove(p)
end if
if dbg then wscript.echo vbTab & p & vbTab & """" & exps(p) & """"
next
'***********************************************************
Phase "Output the ports/MACs list, including unassigned DHCP reservations"
'***********************************************************
LINE = LINE & vbCRLF & vbCRLF & "" & vbCRLF _
& "" & th & "Reservation" & th & "Reservation Description" & th & "Address" & th & "Ping" & th & "MAC" _
& th & "TCP, Port|LPR, Name" & th & "SNMP" & th & "Driver"
for each p in tcpips.Keys
wscript.echo vbTab & p & vbTab & tcpips(p)
if len(tcpips(p)) then
LINE = LINE & vbCRLF & tcpips(p)
if len(exps(p)) then
LINE = LINE & td & Split(exps(p), td)(4)
else
LINE = LINE & td & divright("unused")
end if
end if
next
LINE = LINE & vbCRLF & " " & vbCRLF
Function divright(s)
divright = "(" & s & " on " & WSH.Environment("Process")("COMPUTERNAME") & ") "
End Function
'***********************************************************
Phase "Actually output the report (" & len(LINE) & ")"
'***********************************************************
WriteStatus "prnt", color, "Printers list (" _
& totals(0) & " shares on " & totals(2) & " MACs, " _
& totals(1) & " unassigned MACs; " & ports.Count & " ports total; " & totals(3) & " jobs)", LINE
'if dbg then wscript.echo LINE
Function dt(t) 'Colorize by age
dt = ""
if not IsNull(t) then
Dim dtt : dtt = CLng(Left(t, 8)) 'Date of submission
Dim d : d = Now : d = 10000 * Year(d) + 100 * Month(d) + Day(d)
Select Case d - dtt
Case 0 : dt = "&green" 'today
Case 1 : dt = "&yellow" 'yesterday
Case 2,3,4,5,6 : dt = "&red" 'past week
Case Else : dt = "&purple" 'long ago
End Select
dt = dt & dtt & " " & Mid(t, 9, 6) & Right(t,4)
end if
End Function
Function pingable(ip)
if not IsNumeric(Left(ip, 1)) then
pingable = "clear" 'Disable test if not an IP address
Exit Function
end if
if pingables.Exists(ip) then
Phase "Already tried to ping " & ip & ", " & pingables(ip)
pingable = pingables(ip)
exit function
end if
'pingable = "green" : exit function
Phase "Try to ping " & ip
'Dim WMI_ : Set WMI_ = GetObject("winmgmts:{impersonationLevel=impersonate}")
'The following should be a WMI Get rather than a query, as only one response is usable
Dim pings : Set pings = WMI.ExecQuery("select * from Win32_PingStatus where address = '" & ip & "'")
Dim pi : for each pi in pings
pingables(ip) = "green"
if 0 <> pi.StatusCode then pingables(ip) = "red"
pingable = pingables(ip)
exit function
next
End Function
'######################################################################
'Write out the status; depends on FSO and WSH; will use TimerStart if available for duration
Sub WriteStatus(column, color, quickie, line)
'Why so long? Assume that client might collect file while writing, so make temp file and rename it.
'######################################################################
Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WSH : Set WSH = CreateObject("WScript.Shell")
Dim HKLMSoft : HKLMSoft="HKLM\SOFTWARE"
if WSH.Environment("SYSTEM")("PROCESSOR_ARCHITECTURE") <> "x86" then HKLMSoft = HKLMSoft & "\Wow6432Node"
Const LEAKYMAX = 8500, BBWinreg = "\BBWin\tmpPath"
Const Questreg = "\Quest Software\BigBrother\bbnt\ExternalPath\"
On error resume next
'Assume that, if BBWin is installed, they're using it; if not then they're using Quest BB client.
Dim colfn, src, leaky : colfn = "" : colfn = WSH.RegRead(HKLMSoft & BBWinreg) : src = "bbwin"
If "" = colfn then : colfn = WSH.RegRead(HKLMSoft & Questreg) : src = "" : end if
If "" = colfn then Exit Sub 'Abort if nothing in the registry for location.
colfn = colfn & "\" & column 'Add the column name to make it a real colfile.
Dim tmpfn : tmpfn = colfn & ".$$$" 'Temporary file; use temporary name for safety!
If FSO.FileExists(tmpfn) Then WScript.Quit 183 'Bail out if already there (i.e. previous stuck)
Phase "Create temporary file with status"
With FSO.CreateTextFile(tmpfn, True)
if err then WScript.Quit err 'Bail out if unable to create file.
if Len(quickie) + Len(line) > LEAKYMAX and src = "bbwin" then
leaky = True
.Write "status " & WSH.RegRead(HKLMSoft & "\BBWin\hostname") & "." & column & " "
end if
.WriteLine color & " " & WeekdayName(Weekday(Now),True) & " " _
& Mid(Now,1,InStr(Now," ")-1) & " " & TimeValue(Now) _
& " [" & WSH.Environment("Process")("COMPUTERNAME") & "] " & quickie
.WriteLine line
.Write "" & WScript.ScriptName & " (modified " & CDate(CLng( _
FSO.GetFile(WScript.ScriptFullName).DateLastModified))
if not IsNull(TimerStart) then .Write "; run time " & Timer - TimerStart & "s" 'midnight bug
.Close
End With
With FSO.OpenTextFile(tmpfn, 8)
.Write "; len ~" & FSO.GetFile(tmpfn).Size + 29 & ")" 'assume 1-9kB
.Close
End With
'***********************************************************
'Rename file so client will see it (taking care to remove any unprocessed file first)
'Or send directly with bbwincmd.exe if over LEAKYMAX and using bbwin
'***********************************************************
Phase "Rename temporary file for automatic send -OR- send directly"
on error goto 0
'wscript.echo len(line)
if leaky then 'if FSO.GetFile(tmpfn).Size > LEAKYMAX and src = "bbwin" then
Dim XML : Set XML = CreateObject("Microsoft.XMLDOM") 'Requires MSIE 5 or later
If XML.Load(WSH.RegRead(HKLMSoft & "\BBWin\etcPath") & "\bbwin.cfg") then
Phase "Status2XML, len=" & FSO.GetFile(tmpfn).Size
Dim x, s : for each x in XML.SelectNodes("//configuration/bbwin/setting[@name='bbdisplay']")
s = x.Attributes.GetNamedItem("value").Value
Phase "upload to " & s & " " & tmpfn
WSH.Run "..\bin\bbwincmd.exe " & s & " uploadmessage """ & tmpfn & """", 1, True
next
Else
WScript.Echo "Error parsing " & WSH.RegRead(HKLMSoft & "\BBWin\etcPath") & "\bbwin.cfg"
End If
FSO.DeleteFile tmpfn
else
if FSO.FileExists(colfn) then FSO.DeleteFile(colfn)
FSO.MoveFile tmpfn, colfn
end if
End Sub
'If the script gets stuck, we can use Process Explorer to examine its environment
Sub Phase(n)
if dbg then wscript.echo vbTab & n
CreateObject("WScript.Shell").Environment("Process")("P-" & WScript.ScriptName) = n & " (" & Now & ")"
End Sub
Function WMIQuery(ByVal s)
Phase "WMI Query: " & s
if Instr(s, ";") > 1 then s = Replace("Select " & s, ";", " from ") else s = "Select * from " & s
Set WMIQuery = WMI.ExecQuery(s) 'This did have a ,,48 but I'm not sure it's worthwhile
Phase "WMI Queried: " & s
End Function
===== Known Bugs and Issues =====
* Figure out what credentials are required to remotely retrieve a DHCP server's reservation list.
* Determine why a WMI query for Win32_Printer sometimes gets stuck.
===== To Do =====
* Confirm that changing td/th to tabs actually does produce readable (non-HTML) output.
* Figure out why the server sometimes claims a printer has gone offline when it hasn't, and won't come back online unless the Spooler service is restarted. This script will consider a printer in this situation (offline but pingable) as having a red status, with a need to restart the Print Spooler service likely.
* Perhaps find more methods that printer driver creators use to embed the version numbers in their drivers.
===== Credits =====
(see source code)
===== Changelog =====
* **2012-05-03**
* Initial release
| |