blob: deec7b27737a03e438bafeacb44f8296bca1fee6 [file] [log] [blame]
# winenum.rb
# enumerate any WMI class in any namespace
#
# Usage
# winenum [--debug] [--namespace <namespace>] <classname> [ <property> ... ]
#
# Example
# (-> http://www.codeproject.com/Articles/46390/WMI-Query-Language-by-Example)
# 1. Enumerate namespaces (call recursively!)
# ruby winenum.rb -n root __NAMESPACE
# 2. Enumerate classnames
# ruby winenum.rb -n root/cimv2 -q "select * from meta_class"
# 3. Get class properties
# ruby winenum.rb -n root/cimv2 -q "select * from meta_class where __Class = \"Win32_LogicalDisk\""
# 4. Get all base classes
# ruby winenum.rb -n root/cimv2 -q "select * from meta_class where __SuperClass Is Null"
# 5. Get immediate children of a class
# ruby winenum.rb -n root/cimv2 -q "select * from meta_class where __SuperClass = \"CIM_Setting\""
# 6. Get the dynasty of children starting at a class
# ruby winenum.rb -n root/cimv2 -q "select * from meta_class where __Dynasty = \"CIM_Setting\""
#
# Additional WQL parameters
# - limit to specific names
# Where __Class Like "Win32%"
# - limit to specific origins ("is a")
# Where __This Isa "__Event"
#
#
# Associations
# winrm enumerate wmicimv2/* -dialect:association -associations -filter:{object=win32_service?name=winrm;resultclassname=win32_dependentservice;role=dependent}
# (a: addressing, b: cimbinding, w: dmtf wsman, p: microsoft wsman)
# <n:Enumerate>
# <w:OptimizeEnumeration/>
# <w:MaxElements>32000</w:MaxElements>
# <w:Filter Dialect="http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter">
# <b:AssociationInstances>
# <b:Object>
# <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
# <a:ReferenceParameters>
# <w:ResourceURI>http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/win32_service</w:ResourceURI>
# <w:SelectorSet>
# <w:Selector Name="name">winrm</w:Selector>
# </w:SelectorSet>
# </a:ReferenceParameters>
# </b:Object>
# <b:ResultClassName>win32_dependentservice</b:ResultClassName>
# <b:Role>dependent</b:Role>
# </b:AssociationInstances>
# </w:Filter>
# </n:Enumerate>
#
# winrm enumerate wmicimv2/* -dialect:association -associations -filter:{object=win32_service?name=winrm}
# <n:Enumerate>
# <w:OptimizeEnumeration/>
# <w:MaxElements>32000</w:MaxElements>
# <w:Filter Dialect="http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter">
# <b:AssociationInstances>
# <b:Object>
# <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
# <a:ReferenceParameters>
# <w:ResourceURI>http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/win32_service</w:ResourceURI>
# <w:SelectorSet>
# <w:Selector Name="name">winrm</w:Selector>
# </w:SelectorSet>
# </a:ReferenceParameters>
# </b:Object>
# </b:AssociationInstances>
# </w:Filter>
# </n:Enumerate></s:Body></s:Envelope>
require 'rexml/document'
require File.expand_path(File.join(File.dirname(__FILE__),'_loadpath'))
require 'openwsman'
require '_client'
require 'getoptlong'
WIDTH = 25
#
# parse classname and selectors from string
#
# <classname>?key=value&key=value...
#
def parse_classname str
raise "Classname required" unless str
classname, args = str.split "?"
return [classname] unless args
selectors = {}
args.split("&").each do |kv|
key,value = kv.split "="
selectors[key] = value
end
[classname,selectors]
end
#
# extract reference information from a node
#
# returns a [ <uri>, <selectors> ] pair
#
def get_reference_from node
uri = node.ReferenceParameters.ResourceURI
selectors = {}
node.ReferenceParameters.SelectorSet.each do |s|
name = s.attr_find(nil, "Name")
selectors[name] = s.text
end
[uri,selectors]
end
#
# Extract filter arguments
# --{associators,references} <ResultClassName>,<Role>[,<ResultRole>]
# e.g. Win32_DependentService,dependent
# e.g. Win32_Service,dependent,antecedent
#
# @returns [AssociationClassName, result_class, role, result_role ]
#
def extract_filter_arguments arg
if arg.nil? || arg.empty?
return nil
end
a,b,c,d = arg.split ","
a = nil if a && a.empty?
b = nil if b && b.empty?
c = nil if c && c.empty?
d = nil if d && d.empty?
[a,b,c,d]
end
def print_item indent, item, properties = []
indentation = " "*indent
if properties.empty?
# puts node.string
item.each do |c|
if c.size == 0
attrs = ""
c.each_attr { |a| attrs << " #{a.name}=#{a.value}" }
attrs = "<"+attrs+">" unless attrs.empty?
puts "#{indentation}#{c.name}#{attrs}: #{c.text}"
else
print_item indent+1, c
end
end
else
values = []
properties.each do |p|
values << item.send(p)
end
printf "#{indentation}%-#{WIDTH}s\n" * values.size, *values
end
end
#
# Enumerate
#
# Enumerates instances of a class
# parms[:epr] - end point references only (aka 'names')
# parms[:associators] - enumerate associators
# parms[:references] - enumerate references
#
def enum_properties client, parms, *properties
options = Openwsman::ClientOptions.new
namespace = parms[:namespace] || "root/cimv2"
classname = parms[:classname]
limit = parms[:limit].to_i rescue 0
faults = 0
filter = nil
if parms[:query]
q = parms[:query]
filter = Openwsman::Filter.new
filter.wql q
classname = "*"
if q =~ /.*from(\s+)(([\w_])+)/i
uri = Openwsman.epr_prefix_for $2, namespace
uri << "/*"
end
end
start_time = Time.new
# Openwsman::debug = -1
# Openwsman.debug = -1
# options.max_envelope_size = 1024 * 1024 * 1024
# puts "max_envelope_size #{options.max_envelope_size}"
options.set_dump_request if parms[:debug]
# timeout
if parms[:timeout]
options.timeout = parms[:timeout].to_i * 1000 # parms is in sec, timeout in msec
end
# locale
if parms[:locale]
options.locale = parms[:locale]
end
options.flags = Openwsman::FLAG_ENUMERATION_OPTIMIZATION
options.max_elements = 999
# return endpoint references (instead of instances)
options.flags = Openwsman::FLAG_ENUMERATION_ENUM_EPR if parms[:epr]
uri ||= Openwsman.epr_uri_for namespace, classname
# enumerate Associators
if parms[:associators] || parms[:references]
# options.flags = Openwsman::FLAG_ENUMERATION_ENUM_OBJ_AND_EPR
filter = Openwsman::Filter.new
selectors = parms[:selectors]
epr = Openwsman::EndPointReference.new(uri, nil, selectors)
if parms[:associators]
assoc_class, result_class, role, result_role = extract_filter_arguments parms[:associators]
filter.associators(epr, assoc_class, result_class, role, result_role)
elsif parms[:references]
assoc_class, result_class, role, result_role = extract_filter_arguments parms[:references]
filter.references(epr, assoc_class, result_class, role, result_role)
end
classname = "*" # accept any result classname
uri = Openwsman.epr_uri_for "", classname
end
# enumerate References
# STDERR.puts "URI <#{uri}>"
result = client.enumerate( options, filter, uri )
unless properties.empty?
printf "%-#{WIDTH}s" * properties.size, *properties
puts
puts "-" * WIDTH * properties.size
end
results = 0
context = nil
loop do
break if fault? client, result
puts "result #{result.to_xml}" if parms[:debug]
items = result.Items
break unless items
items.each do |item|
if parms[:epr]
epr = Openwsman::EndPointReference.new(item)
puts epr
results += 1
else
# puts items.to_xml if parms[:debug]
puts "-------" if results > 0
unless classname == "*"
next unless item.name == classname
end
puts item.name
results += 1
print_item 1, item, properties
end
end # Items.each
limit -= 1
break if limit == 0
context = result.context
break unless context
result = client.pull( options, nil, uri, context )
puts
end
client.release( options, uri, context ) if context
duration = Time.new - start_time
printf "%d results, %d faults in %3.2f seconds\n" % [results, faults, duration]
end
def usage msg=nil
STDERR.puts msg if msg
STDERR.puts "Usage:"
STDERR.puts "winenum [-n <namespace>] [-q <wql-query>] [-l <limit>] [-d] <class>"
STDERR.puts "\twith -q <query>, <class> is discarded"
STDERR.puts "\nand remember to set WSMANCLIENT"
exit 1
end
#
# --- main ---
#
#
client = Client.open
parms = {}
begin
opts = GetoptLong.new(
[ "--associators", "-a", GetoptLong::REQUIRED_ARGUMENT ],
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
[ "--epr", "-e", GetoptLong::NO_ARGUMENT ],
[ "--limit", "-l", GetoptLong::REQUIRED_ARGUMENT ],
[ "--namespace", "-n", GetoptLong::REQUIRED_ARGUMENT ],
[ "--query", "-q", GetoptLong::REQUIRED_ARGUMENT ],
[ "--references", "-r", GetoptLong::REQUIRED_ARGUMENT ],
[ "--timeout", "-t", GetoptLong::REQUIRED_ARGUMENT ],
[ "--locale", "-L", GetoptLong::REQUIRED_ARGUMENT ]
)
opts.each do |opt,arg|
case opt
when "--query" then parms[:query] = arg
when "--limit" then parms[:limit] = arg
when "--namespace" then parms[:namespace] = arg
when "--debug" then parms[:debug] = true
when "--epr" then parms[:epr] = true
when "--associators" then parms[:associators] = arg
when "--references" then parms[:references] = arg
when "--timeout" then parms[:timeout] = arg
when "--locale" then parms[:locale] = arg
end
end
rescue GetoptLong::InvalidOption
usage "invalid option passed"
end
if parms[:associators] && parms[:references]
usage "Enumeration of Associators and References not supported"
end
unless parms[:query]
parms[:classname], parms[:selectors] = parse_classname(ARGV.shift)
end
#puts "Classname(#{parms[:classname]}) Selectors(#{parms[:selectors].inspect})"
enum_properties client, parms, *ARGV