hawatel_ps 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +184 -0
- data/Rakefile +12 -0
- data/hawatel_ps.gemspec +29 -0
- data/lib/hawatel_ps/linux/proc_control.rb +75 -0
- data/lib/hawatel_ps/linux/proc_fetch.rb +405 -0
- data/lib/hawatel_ps/linux/proc_info.rb +53 -0
- data/lib/hawatel_ps/linux/proc_table.rb +124 -0
- data/lib/hawatel_ps/linux.rb +4 -0
- data/lib/hawatel_ps/shared/hawatelps_exception.rb +21 -0
- data/lib/hawatel_ps/version.rb +3 -0
- data/lib/hawatel_ps/windows/proc_control.rb +67 -0
- data/lib/hawatel_ps/windows/proc_fetch.rb +217 -0
- data/lib/hawatel_ps/windows/proc_info.rb +51 -0
- data/lib/hawatel_ps/windows/proc_table.rb +138 -0
- data/lib/hawatel_ps/windows/wmi/wmi_cli.rb +65 -0
- data/lib/hawatel_ps/windows/wmi/wmi_exception.rb +23 -0
- data/lib/hawatel_ps/windows/wmi/wmi_instance.rb +56 -0
- data/lib/hawatel_ps/windows.rb +5 -0
- data/lib/hawatel_ps.rb +34 -0
- data/spec/hawatel_ps_spec.rb +11 -0
- data/spec/linux/bdd/proc_spec.rb +54 -0
- data/spec/linux/factories/etc/passwd +32 -0
- data/spec/linux/factories/proc/1761/cmdline +0 -0
- data/spec/linux/factories/proc/1761/environ +0 -0
- data/spec/linux/factories/proc/1761/io +7 -0
- data/spec/linux/factories/proc/1761/limits +17 -0
- data/spec/linux/factories/proc/1761/stat +1 -0
- data/spec/linux/factories/proc/1761/status +46 -0
- data/spec/linux/factories/proc/meminfo +45 -0
- data/spec/linux/factories/proc/net/tcp +7 -0
- data/spec/linux/factories/proc/net/udp +8 -0
- data/spec/linux/factories/proc/uptime +1 -0
- data/spec/linux/support/stub_dir.rb +33 -0
- data/spec/linux/support/stub_file.rb +107 -0
- data/spec/linux/tdd/proc_fetch_spec.rb +107 -0
- data/spec/linux/tdd/proc_table_spec.rb +85 -0
- data/spec/shared/hawatelps_exception_spec.rb +13 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/windows/bdd/proc_spec.rb +119 -0
- data/spec/windows/factories/proc_fetch_factorie.rb +76 -0
- data/spec/windows/tdd/proc_control_spec.rb +36 -0
- data/spec/windows/tdd/proc_fetch_spec.rb +73 -0
- data/spec/windows/tdd/proc_table_spec.rb +71 -0
- data/spec/windows/tdd/wmi_spec.rb +59 -0
- metadata +181 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
##
|
4
|
+
# = Process Fetch
|
5
|
+
#
|
6
|
+
# Provides functionality to fetch process information from raw source using WMI client.
|
7
|
+
class ProcFetch
|
8
|
+
class << self
|
9
|
+
# Return attributes of all processes from WMI
|
10
|
+
#
|
11
|
+
# Each process has attributes which are standardized in this document:
|
12
|
+
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa394372
|
13
|
+
# Each attribute name is converted to lowercase.
|
14
|
+
#
|
15
|
+
# @example Get process list (access to attributes by index array)
|
16
|
+
# processes = get_process()
|
17
|
+
# processes.each do | process |
|
18
|
+
# pid = process['processid']
|
19
|
+
# name = process['name']
|
20
|
+
# puts "#{pid.to_s.ljust(10)} #{name}"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Get process list (access to attributes by methods)
|
24
|
+
# processes = get_process()
|
25
|
+
# processes.each do | process |
|
26
|
+
# pid = process.processid
|
27
|
+
# name = process.name
|
28
|
+
# puts "#{pid.to_s.ljust(10)} #{name}"
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @return [Array<Hash>]
|
32
|
+
def get_process(args = nil)
|
33
|
+
if args.nil?
|
34
|
+
wql = prepare_wql("SELECT * FROM Win32_Process")
|
35
|
+
else
|
36
|
+
wql = prepare_wql('SELECT * FROM Win32_Process', args)
|
37
|
+
end
|
38
|
+
|
39
|
+
proc_table = Array.new
|
40
|
+
|
41
|
+
# TODO maybe the better way will be put user info to class variable?
|
42
|
+
@users_list = get_users
|
43
|
+
@system_info = system_info
|
44
|
+
@memory_total = @system_info[:totalvisiblememorysize]
|
45
|
+
@system_idle_time = system_idle_time
|
46
|
+
|
47
|
+
WmiCli.new.query(wql).each do |proc_instance|
|
48
|
+
proc = extract_wmi_property(proc_instance)
|
49
|
+
# if proces no longer exists it won't be added to the resulting array
|
50
|
+
# sometimes it happens when the Win32_Process query returned process but
|
51
|
+
# when this program tries invoke execMethod_ on it the WmiCli class raises an exception
|
52
|
+
# because the process disappeared
|
53
|
+
proc_table.push(proc) if !proc.nil?
|
54
|
+
end
|
55
|
+
proc_table
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Prepare WMI Query Language
|
61
|
+
# @param query [String] WQL string
|
62
|
+
# @param args [Hash] conditions to WHERE clause (conditions are combined only with AND operator)
|
63
|
+
# @option name [Type] :opt description
|
64
|
+
# @example
|
65
|
+
# prepare_wql('SELECT * FROM Win32_Process')
|
66
|
+
# prepare_wql('SELECT * FROM Win32_Process', {:processid => 1020})
|
67
|
+
# prepare_wql('SELECT * FROM Win32_Process', {:name => 'notepad.exe', :executablepath => 'C:\\\\WINDOWS\\\\system32\\\\notepad.exe'})
|
68
|
+
# @return [String] WQL string
|
69
|
+
def prepare_wql(query, args = nil)
|
70
|
+
if args.nil?
|
71
|
+
return query
|
72
|
+
else
|
73
|
+
query += " WHERE "
|
74
|
+
args.each_with_index do |(k, v), index|
|
75
|
+
if index == 0 && args.length == 1
|
76
|
+
query += "#{k.to_s.downcase} = '#{v}'"
|
77
|
+
elsif index == args.length - 1
|
78
|
+
query += "#{k.to_s.downcase} = '#{v}'"
|
79
|
+
else
|
80
|
+
query += "#{k.to_s.downcase} = '#{v}' AND "
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return query
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get property from WIN32OLE object about process
|
88
|
+
# @param wmi_object [WmiCli::Instance] process instance represented by WMI object
|
89
|
+
# @example
|
90
|
+
# extract_wmi_property(wmi_object)
|
91
|
+
# @return [Hash]
|
92
|
+
def extract_wmi_property(wmi_object)
|
93
|
+
property_map = wmi_object.properties.dup
|
94
|
+
property_map[:wmi_object] = wmi_object.wmi_ole_object
|
95
|
+
property_map[:childs] = Array.new
|
96
|
+
|
97
|
+
property_map[:sid] = get_owner_sid(wmi_object)
|
98
|
+
|
99
|
+
get_owner(property_map)
|
100
|
+
property_map[:availablevirtualsize] = get_avail_virtual_size(wmi_object)
|
101
|
+
property_map[:memorypercent] = memory_percent(@memory_total, property_map[:workingsetsize])
|
102
|
+
|
103
|
+
property_map[:cpupercent] = cpu_percent(cpu_time(
|
104
|
+
:usermodetime => property_map[:usermodetime],
|
105
|
+
:kernelmodetime => property_map[:kernelmodetime]))
|
106
|
+
|
107
|
+
hash_value_to_string(property_map)
|
108
|
+
|
109
|
+
property_map.delete(:status) if property_map.key?(:status)
|
110
|
+
|
111
|
+
property_map
|
112
|
+
end
|
113
|
+
|
114
|
+
# Convert hash values to string if is the Integer type
|
115
|
+
# @param hash [Hash]
|
116
|
+
def hash_value_to_string(hash)
|
117
|
+
hash.each {|k,v| hash[k] = v.to_s if v.is_a?(Integer)}
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get users list from Win32_UserAccount class
|
121
|
+
# @return [Hash] @users_list
|
122
|
+
def get_users
|
123
|
+
users_list = Hash.new
|
124
|
+
WmiCli.new.query('SELECT * FROM Win32_UserAccount').each do |user|
|
125
|
+
users_list[user.properties[:sid]] = user.properties if !user.nil?
|
126
|
+
end
|
127
|
+
users_list
|
128
|
+
end
|
129
|
+
|
130
|
+
# Find information about user
|
131
|
+
# instance variable @users_list must be set {get_users}
|
132
|
+
# @param property_map [Hash] Atributes of process
|
133
|
+
def get_owner(property_map)
|
134
|
+
property_map[:user] = nil
|
135
|
+
property_map[:domain] = nil
|
136
|
+
|
137
|
+
if !property_map[:sid].nil? and !@users_list[property_map[:sid]].nil?
|
138
|
+
property_map[:user] = @users_list[property_map[:sid]][:name]
|
139
|
+
property_map[:domain] = @users_list[property_map[:sid]][:domain]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Invoke GetOwnerSid method of the Win32_Process class
|
144
|
+
# @see https://msdn.microsoft.com/pl-pl/library/windows/desktop/aa390460
|
145
|
+
# @param wmi_object [WmiCli::Instance] process instance represented by WMI object
|
146
|
+
# @return [String] SID of user
|
147
|
+
# @reutrn [nil] if wmi_object no longer exists
|
148
|
+
def get_owner_sid(wmi_object)
|
149
|
+
# TODO performance problem (it takes ~10 seconds but the native wmi takes similar time)
|
150
|
+
owner = wmi_object.execMethod('GetOwnerSid')
|
151
|
+
return owner.Sid if !owner.nil?
|
152
|
+
rescue WmiCliException
|
153
|
+
return nil
|
154
|
+
end
|
155
|
+
|
156
|
+
# Invoke GetAvailableVirtualSize method of the Win32_Process class
|
157
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/dn434274
|
158
|
+
# @param wmi_object [WmiCli::Instance] process instance represented by WMI object
|
159
|
+
# @return [String] AvailableVirtualSize from WMI
|
160
|
+
# @reutrn [nil] if wmi_object no longer exists
|
161
|
+
def get_avail_virtual_size(wmi_object)
|
162
|
+
obj = wmi_object.execMethod('GetAvailableVirtualSize')
|
163
|
+
return obj.AvailableVirtualSize.to_s if !obj.nil?
|
164
|
+
rescue WmiCliException
|
165
|
+
return nil
|
166
|
+
end
|
167
|
+
|
168
|
+
# System information from Win32_OperatingSystem class
|
169
|
+
# @return [Hash]
|
170
|
+
def system_info
|
171
|
+
WmiCli.new.query('SELECT * FROM Win32_OperatingSystem').each do |result|
|
172
|
+
return result.properties if !result.nil?
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Return percent of memory usage by process
|
177
|
+
# @return [String]
|
178
|
+
def memory_percent(mem_total_kb, workingsetsize)
|
179
|
+
if !mem_total_kb.nil? && !workingsetsize.nil? && mem_total_kb.to_i > 0
|
180
|
+
rss_kb = workingsetsize.to_f / 1024
|
181
|
+
return (rss_kb / mem_total_kb.to_f * 100).round(2).to_s
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Calculate cpu time for System Idle Process
|
186
|
+
# @return [Integer] system idle time in seconds
|
187
|
+
def system_idle_time
|
188
|
+
WmiCli.new.query("SELECT KernelModeTime FROM Win32_Process WHERE ProcessId = '0'").each do |idle|
|
189
|
+
return (idle.properties[:kernelmodetime].to_i / 10000000)
|
190
|
+
end
|
191
|
+
return nil
|
192
|
+
end
|
193
|
+
|
194
|
+
# Calculate %CPU usage per process
|
195
|
+
# @param cpu_time [String/Integer] CPU time consumed by process since system boot
|
196
|
+
# @return [String] %CPU usage per process since system boot
|
197
|
+
def cpu_percent(cpu_time)
|
198
|
+
if !cpu_time.zero?
|
199
|
+
return (( cpu_time.to_f / @system_idle_time.to_f) * 100).round(2).to_s
|
200
|
+
else
|
201
|
+
return "0.0"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Reports processor use time, in seconds, for each process running on a computer.
|
206
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa394599(v=vs.85)
|
207
|
+
# @param args [Hash] attributes
|
208
|
+
# @option opt [String/Integer] User Mode Time
|
209
|
+
# @option opt [String/Integer] Kernel Mode Time
|
210
|
+
# @return [Integer] processor time for a process in seconds
|
211
|
+
def cpu_time(args)
|
212
|
+
return ((args[:usermodetime].to_i + args[:kernelmodetime].to_i) / 10000000)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
class ProcInfo < ProcControl
|
4
|
+
# Process instance with attributes of that process
|
5
|
+
# @param proc_attrs [Hash] attributes of the process
|
6
|
+
# @return [void]
|
7
|
+
def initialize(proc_attrs)
|
8
|
+
@proc_attrs = proc_attrs
|
9
|
+
define_attributes(proc_attrs)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Make attributes of public.
|
13
|
+
# Access to process attribute from an object instance by index of array.
|
14
|
+
# @example
|
15
|
+
# p = ProcInfo.new(proc_attrs)
|
16
|
+
# puts p['processid']
|
17
|
+
def [](key)
|
18
|
+
key = key.to_s.downcase.to_sym if !key.is_a?(Symbol)
|
19
|
+
@proc_attrs[key.downcase]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Calls the given block once for each element in self, passing that element as a parameter.
|
23
|
+
# @param &block
|
24
|
+
# @example print all attributes of process
|
25
|
+
# p = ProcInfo.new(proc_attrs)
|
26
|
+
# proc.each {|key, val| puts "#{key} - #{val}"}
|
27
|
+
# @return An Enumerator is returned if no block is given.
|
28
|
+
def each(&block)
|
29
|
+
@proc_attrs.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @see ProcInfo#define_attributes
|
33
|
+
def metaclasses
|
34
|
+
class << self; self; end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
# Make attributes of public.
|
39
|
+
# Access to process attribute from an object instance by public method where names of attributes are methods.
|
40
|
+
# @example
|
41
|
+
# p = ProcInfo.new(proc_attrs)
|
42
|
+
# puts p.processid
|
43
|
+
def define_attributes(hash)
|
44
|
+
hash.each_pair do |key, value|
|
45
|
+
metaclasses.send(:attr_reader, key.to_sym)
|
46
|
+
instance_variable_set("@#{key}", value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
class ProcTable
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Return attributes of searched process based on pid
|
7
|
+
# @example
|
8
|
+
# search_by_pid(1761)
|
9
|
+
# @param [Integer] pid of process
|
10
|
+
# @return [ProcInfo]
|
11
|
+
def search_by_pid(pid)
|
12
|
+
find_by_pid(pid)
|
13
|
+
@proc_table.each do |process|
|
14
|
+
return process if process.processid.to_s == pid.to_s
|
15
|
+
end
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Return attributes of searched process based on name or cmdline
|
21
|
+
# @example
|
22
|
+
# search_by_name('java.exe')
|
23
|
+
# search_by_name('/^regex/')
|
24
|
+
# @param process_name[String] name of process
|
25
|
+
# @return [Array<ProcInfo>]
|
26
|
+
def search_by_name(process_name)
|
27
|
+
if process_name =~ /^\/.*\/$/
|
28
|
+
process_name.slice!(0)
|
29
|
+
process_name = Regexp.new(/#{process_name.chop}/)
|
30
|
+
find_all
|
31
|
+
else
|
32
|
+
find_by_name(process_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
process_list = Array.new
|
36
|
+
|
37
|
+
@proc_table.each do |process|
|
38
|
+
if process_name.is_a? Regexp
|
39
|
+
process_list << process if process.name =~ process_name || process.commandline =~ process_name
|
40
|
+
else
|
41
|
+
process_list << process if process.name.to_s.downcase == "#{process_name.to_s.downcase}" || process.commandline.to_s.downcase == "#{process_name.to_s.downcase}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
process_list = nil if process_list.empty?
|
46
|
+
|
47
|
+
return process_list
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return attributes of searched by process
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# search_by_condition(:attrs => 'workingsetsize', :oper => '<', value => '10000')
|
54
|
+
#
|
55
|
+
# @param args[Hash] attributes for search condition
|
56
|
+
# @attrs [String], name of process attribute (first expression for if)
|
57
|
+
# @oper [String], operatator, available options: >,<,>=,<=,==,!=
|
58
|
+
# @value [String], value comparable (second expression for if)
|
59
|
+
#
|
60
|
+
# @return [Array<ProcInfo>]
|
61
|
+
def search_by_condition(args)
|
62
|
+
find_all
|
63
|
+
|
64
|
+
attrs = args[:attr]
|
65
|
+
oper = args[:oper]
|
66
|
+
value = args[:value]
|
67
|
+
process_list = Array.new
|
68
|
+
@proc_table.each do |process|
|
69
|
+
if oper == '>'
|
70
|
+
process_list << process if process[:"#{attrs}"] > value
|
71
|
+
elsif oper == '<'
|
72
|
+
process_list << process if process[:"#{attrs}"] < value
|
73
|
+
elsif oper == '>='
|
74
|
+
process_list << process if process[:"#{attrs}"] >= value
|
75
|
+
elsif oper == '<='
|
76
|
+
process_list << process if process[:"#{attrs}"] <= value
|
77
|
+
elsif oper == '=='
|
78
|
+
process_list << process if process[:"#{attrs}"] == value
|
79
|
+
elsif oper == '!='
|
80
|
+
process_list << process if process[:"#{attrs}"] != value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
process_list = nil if process_list.empty?
|
85
|
+
|
86
|
+
return process_list
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return all process instances
|
90
|
+
# @return [Array<ProcInfo>]
|
91
|
+
def proc_table
|
92
|
+
find_all
|
93
|
+
return @proc_table
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
# Refresh processes array by pid
|
98
|
+
def find_by_pid(pid)
|
99
|
+
@proc_table = Array.new
|
100
|
+
ProcFetch.get_process(:processid => pid).each do |proc_attrs|
|
101
|
+
@proc_table.push(ProcInfo.new(proc_attrs))
|
102
|
+
end
|
103
|
+
childs_tree
|
104
|
+
end
|
105
|
+
|
106
|
+
# Refresh processes array by name
|
107
|
+
def find_by_name(name)
|
108
|
+
@proc_table = Array.new
|
109
|
+
ProcFetch.get_process({:name => name}).each do |proc_attrs|
|
110
|
+
@proc_table.push(ProcInfo.new(proc_attrs))
|
111
|
+
end
|
112
|
+
childs_tree
|
113
|
+
end
|
114
|
+
|
115
|
+
# Refresh processes array
|
116
|
+
def find_all
|
117
|
+
@proc_table = Array.new
|
118
|
+
ProcFetch.get_process.each do |proc_attrs|
|
119
|
+
@proc_table.push(ProcInfo.new(proc_attrs))
|
120
|
+
end
|
121
|
+
childs_tree
|
122
|
+
end
|
123
|
+
|
124
|
+
# Get process childs
|
125
|
+
def childs_tree
|
126
|
+
@proc_table.each do |proc_parent|
|
127
|
+
@proc_table.each do |proc_child|
|
128
|
+
if proc_parent.processid === proc_child.parentprocessid
|
129
|
+
proc_parent[:childs].push(proc_child)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
require 'hawatel_ps/windows/wmi/wmi_instance'
|
3
|
+
require 'hawatel_ps/windows/wmi/wmi_exception'
|
4
|
+
|
5
|
+
module HawatelPS
|
6
|
+
module Windows
|
7
|
+
##
|
8
|
+
# = Windows Management Instrumentation Client
|
9
|
+
#
|
10
|
+
# This is wrapper for WMI with limited functionalities (only call queries)
|
11
|
+
class WmiCli
|
12
|
+
# Init WMI namespace
|
13
|
+
# @param namespace [String] WMI namespace
|
14
|
+
def initialize(namespace = nil)
|
15
|
+
@namespace = namespace.nil? ? 'root/cimv2' : namespace
|
16
|
+
@session = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Call WMI query
|
20
|
+
# @param wql_query [String] WMI Query Language string
|
21
|
+
# @example
|
22
|
+
# WmiCli.new.query('SELECT * FROM Win32_Process')
|
23
|
+
# @raise [WmiCliException] if WQL Query is wrong
|
24
|
+
# @return [WmiCli::Instance]
|
25
|
+
def query(wql_query)
|
26
|
+
connect_to_namespace
|
27
|
+
results = @session.ExecQuery(wql_query)
|
28
|
+
|
29
|
+
wmi_ole_instances = create_wmi_ole_instances(results)
|
30
|
+
rescue WIN32OLERuntimeError => ex
|
31
|
+
raise WmiCliException, :exception => ex, :message => "Wrong result from WQL Query = '#{wql_query}'."
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Connect to the WMI on the local server
|
37
|
+
# @example
|
38
|
+
# connect_to_namespace
|
39
|
+
# @raise [WIN32OLERuntimeError] if problem with connection to the local server
|
40
|
+
# @return [WIN32OLE]
|
41
|
+
def connect_to_namespace
|
42
|
+
if @session.nil?
|
43
|
+
locator = WIN32OLE.new("WbemScripting.SWbemLocator")
|
44
|
+
@session = locator.ConnectServer('.', @namespace)
|
45
|
+
end
|
46
|
+
rescue WIN32OLERuntimeError => ex
|
47
|
+
raise WmiCliException, :exception => ex, :message => "Cannot connect to namespace '#{@namespace}'."
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create the WMI32OLE instance from a data set.
|
51
|
+
# @param dataset [WMI32OLE] List of WMI objects
|
52
|
+
# @example
|
53
|
+
# results = @session.ExecQuery(wql_query)
|
54
|
+
# wmi_ole_instances = create_wmi_ole_instances(results)
|
55
|
+
# @return [Array<WmiCli::Instance>]
|
56
|
+
def create_wmi_ole_instances(dataset)
|
57
|
+
instances = []
|
58
|
+
dataset.each do |wmi_object|
|
59
|
+
instances.push(Instance.new(wmi_object))
|
60
|
+
end
|
61
|
+
instances
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
class WmiCliException < Exception
|
4
|
+
# Custom exception
|
5
|
+
# @param args [Hash] the options to create a custom exception message
|
6
|
+
# @option :exception [Exception] Native Exception object
|
7
|
+
# @option :message [String] Custom message
|
8
|
+
# @return [void]
|
9
|
+
def initialize(args = {:exception => nil, :message => nil})
|
10
|
+
super(exception_enrichment(args))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def exception_enrichment(args)
|
15
|
+
error_message = ''
|
16
|
+
error_message += args[:message] unless args[:message].nil?
|
17
|
+
error_message +=
|
18
|
+
"\nNative exception from '#{args[:exception].class}':\n#{args[:exception].message}" unless args[:exception].nil?
|
19
|
+
return error_message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
class WmiCli
|
4
|
+
##
|
5
|
+
# = Instance of Windows Management Instrumentation client
|
6
|
+
#
|
7
|
+
# Container of single WIN32OLE object wih parameters of this object
|
8
|
+
#
|
9
|
+
# @!attribute [r] wmi_ole_object
|
10
|
+
# @return [WIN32OLE] WMI Object
|
11
|
+
# @!attribute [r] properties
|
12
|
+
# @return [Hash] Properties from WIN32OLE object
|
13
|
+
class Instance
|
14
|
+
attr_reader :wmi_ole_object, :properties
|
15
|
+
|
16
|
+
# Create instance of WmiClient class with WIN32OLE object
|
17
|
+
# @param wmi_ole_object [WIN32OLE] Single WMI32OLE object
|
18
|
+
def initialize(wmi_ole_object)
|
19
|
+
@wmi_ole_object = wmi_ole_object
|
20
|
+
@properties = extract_props_to_hash(wmi_ole_object)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Execute WIN32OLE method
|
24
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa393774
|
25
|
+
# @param name [String] WMI method name assigned to WIN32OLE object
|
26
|
+
# @example Object if from Win32_Process class and it has 'GetOwner' method
|
27
|
+
# wmi_object.execMethod('GetOwner')
|
28
|
+
# @return [WIN32OLE]
|
29
|
+
def execMethod(name)
|
30
|
+
result = @wmi_ole_object.execMethod_(name) if @wmi_ole_object.ole_respond_to?('execMethod_')
|
31
|
+
rescue WIN32OLERuntimeError => ex
|
32
|
+
raise WmiCliException, :exception => ex, :message => "Cannot invoke execMethod_('#{name}')"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Get property from WIN32OLE object
|
38
|
+
# @param wmi_object [WIN32OLE] WMI object
|
39
|
+
# @example
|
40
|
+
# extract_props_to_hash(wmi_object)
|
41
|
+
# @return [Hash]
|
42
|
+
def extract_props_to_hash(wmi_obj)
|
43
|
+
properties = {}
|
44
|
+
#binding.pry
|
45
|
+
if wmi_obj.ole_respond_to?('properties_')
|
46
|
+
wmi_obj.properties_.each do |property|
|
47
|
+
properties[property.name.downcase.to_sym] = property.value
|
48
|
+
end
|
49
|
+
properties.freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/hawatel_ps.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "hawatel_ps/version"
|
2
|
+
require "hawatel_ps/linux" if RUBY_PLATFORM =~ /linux/
|
3
|
+
require "hawatel_ps/windows" if RUBY_PLATFORM =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
4
|
+
require "hawatel_ps/shared/hawatelps_exception"
|
5
|
+
|
6
|
+
module HawatelPS
|
7
|
+
|
8
|
+
def self.search_by_pid(pid)
|
9
|
+
HawatelPS::platform::ProcTable.search_by_pid(pid)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.search_by_name(name)
|
13
|
+
HawatelPS::platform::ProcTable.search_by_name(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.search_by_condition(args)
|
17
|
+
HawatelPS::platform::ProcTable.search_by_condition(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.proc_table
|
21
|
+
HawatelPS::platform::ProcTable.proc_table
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.platform
|
25
|
+
if RUBY_PLATFORM =~ /linux/
|
26
|
+
Linux
|
27
|
+
elsif RUBY_PLATFORM =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
28
|
+
Windows
|
29
|
+
else
|
30
|
+
raise HawatelPSException.new({:message => "Your OS(#{RUBY_PLATFORM}) is not supported!"})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HawatelPS::Linux::ProcFetch do
|
4
|
+
|
5
|
+
it "list processes" do
|
6
|
+
processes = HawatelPS.proc_table
|
7
|
+
processes.each do |process|
|
8
|
+
expect(process.pid).to be_a_kind_of(Integer)
|
9
|
+
end
|
10
|
+
expect(processes.size).to be >= 2
|
11
|
+
end
|
12
|
+
|
13
|
+
it "search process by pid" do
|
14
|
+
pid = child(5)
|
15
|
+
process = HawatelPS.search_by_pid(pid)
|
16
|
+
expect(process[:pid]).to eq(pid)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "search process by name" do
|
20
|
+
pid = child(5)
|
21
|
+
child_exist = 0
|
22
|
+
processes = HawatelPS.search_by_name('/ruby/')
|
23
|
+
processes.each do |process|
|
24
|
+
child_exist= 1 if process.pid == pid
|
25
|
+
end
|
26
|
+
expect(processes.size).to be >= 1
|
27
|
+
expect(child_exist).to eq(1)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "search process by condition" do
|
31
|
+
processes = HawatelPS.search_by_condition(:attr => 'pid', :oper => '>', :value => '1' )
|
32
|
+
expect(processes.size).to be >= 2
|
33
|
+
end
|
34
|
+
|
35
|
+
it "suspend & resume and terminate process" do
|
36
|
+
pid = child(5)
|
37
|
+
process = HawatelPS.search_by_pid(pid)
|
38
|
+
suspend_status = process.suspend
|
39
|
+
resume_status = process.resume
|
40
|
+
terminate_status = process.terminate
|
41
|
+
expect(suspend_status).to eq('stopped')
|
42
|
+
expect(resume_status).to match(/(sleeping|running)/)
|
43
|
+
expect(terminate_status).to match(/(terminated|zombie)/)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def child(timeout)
|
50
|
+
pid = fork do
|
51
|
+
sleep(timeout)
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|