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,405 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Linux
|
3
|
+
class ProcFetch
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Genererate ProcInfo objects list
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# get_process.each do |process|
|
10
|
+
# p process.pid
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @return [Array<ProcInfo>] - list current running processes
|
14
|
+
def get_process
|
15
|
+
proc_table = Array.new
|
16
|
+
memtotal = memory_total
|
17
|
+
sockets = open_ports
|
18
|
+
Dir.foreach("/proc").each do |pid|
|
19
|
+
if is_numeric?(pid)
|
20
|
+
attrs = Hash.new
|
21
|
+
attrs[:pid] = pid.to_i
|
22
|
+
attrs[:cwd] = process_cwd(pid)
|
23
|
+
attrs[:username] = process_username(pid)
|
24
|
+
attrs[:cmdline] = process_cmdline(pid)
|
25
|
+
attrs[:ctime] = process_ctime(pid)
|
26
|
+
attrs[:limits] = process_limits(pid)
|
27
|
+
attrs[:environ] = process_env(pid)
|
28
|
+
attrs[:childs] = Array.new
|
29
|
+
process_io(attrs)
|
30
|
+
process_files(attrs, sockets)
|
31
|
+
process_status(attrs)
|
32
|
+
process_stat(attrs)
|
33
|
+
attrs[:memory_percent] = memory_percent(attrs, memtotal)
|
34
|
+
proc_table << attrs
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return proc_table
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
# @see https://www.kernel.org/doc/Documentation/filesystems/proc.txt Table 1-2
|
42
|
+
# Get process attributes from /proc/<pid>/status file and save in Hash container
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# process_status(Hash)
|
46
|
+
# @param attrs [Hash] hash list contains process attributes
|
47
|
+
def process_status(attrs)
|
48
|
+
status_file = "/proc/#{attrs[:pid]}/status"
|
49
|
+
File.foreach(status_file).each do |attr|
|
50
|
+
if attr =~ /Name:/
|
51
|
+
attrs[:name] = attr.split(' ')[1]
|
52
|
+
elsif attr =~ /PPid:/
|
53
|
+
attrs[:ppid] = attr.split(' ')[1].to_i
|
54
|
+
elsif attr =~ /State:/
|
55
|
+
attrs[:state] = attr.split(' ')[2].to_s.chop[1..-1]
|
56
|
+
elsif attr =~ /Uid:/
|
57
|
+
attrs[:ruid] = attr.split(' ')[1].to_i
|
58
|
+
attrs[:euid] = attr.split(' ')[2].to_i
|
59
|
+
attrs[:suid] = attr.split(' ')[3].to_i
|
60
|
+
attrs[:fsuid] = attr.split(' ')[4].to_i
|
61
|
+
elsif attr =~ /Gid:/
|
62
|
+
attrs[:gid] = attr.split(' ')[1].to_i
|
63
|
+
attrs[:egid] = attr.split(' ')[2].to_i
|
64
|
+
attrs[:sgid] = attr.split(' ')[3].to_i
|
65
|
+
attrs[:fsgid] = attr.split(' ')[4].to_i
|
66
|
+
elsif attr =~ /Threads:/
|
67
|
+
attrs[:threads] = attr.split(' ')[1].to_i
|
68
|
+
elsif attr =~ /VmSize:/
|
69
|
+
attrs[:vmsize] = attr.split(' ')[1].to_i
|
70
|
+
elsif attr =~ /VmRSS:/
|
71
|
+
attrs[:vmrss] = attr.split(' ')[1].to_i
|
72
|
+
elsif attr =~ /VmData:/
|
73
|
+
attrs[:vmdata] = attr.split(' ')[1].to_i
|
74
|
+
elsif attr =~ /VmSwap:/
|
75
|
+
attrs[:vmswap] = attr.split(' ')[1].to_i
|
76
|
+
elsif attr =~ /VmLib:/
|
77
|
+
attrs[:vmlib] = attr.split(' ')[1].to_i
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @note read access to io file are restricted only to owner of process
|
83
|
+
# Read I/O attributes from /proc/<pid>/io file and save in attrs container
|
84
|
+
#
|
85
|
+
# @example
|
86
|
+
# attrs = Hash.new
|
87
|
+
# process_io(Hash)
|
88
|
+
# p attrs[:wchar]
|
89
|
+
#
|
90
|
+
# @param attrs [Hash] hash list contains process attributes
|
91
|
+
def process_io(attrs)
|
92
|
+
process_io_set_nil(attrs)
|
93
|
+
io_file = "/proc/#{attrs[:pid]}/io"
|
94
|
+
if File.readable?(io_file)
|
95
|
+
File.foreach(io_file).each do |attr|
|
96
|
+
name = attr.split(' ')[0].chop
|
97
|
+
attrs[:"#{name}"] = attr.split(' ')[1].to_i
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Set default value for i/o attributes in attrs container
|
103
|
+
#
|
104
|
+
# @param attrs [Hash] hash list contains process attributes
|
105
|
+
def process_io_set_nil(attrs)
|
106
|
+
['rchar','wchar','syscr','syscw','read_bytes','write_bytes','cancelled_write_bytes'].each do |attr|
|
107
|
+
attrs[:"#{attr}"] = 'Permission denied'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @see https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
112
|
+
# Read statistics from /proc/<pid>/stat file and save in attrs container
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# container = Hash.new
|
116
|
+
# process_stat(container)
|
117
|
+
#
|
118
|
+
# @param attrs [Hash] hash list contains process attributes
|
119
|
+
def process_stat(attrs)
|
120
|
+
stat_file = "/proc/#{attrs[:pid]}/stat"
|
121
|
+
if File.readable? (stat_file)
|
122
|
+
File.foreach(stat_file).each do |line|
|
123
|
+
attr = line.split(' ')
|
124
|
+
attrs[:utime] = attr[13].to_i
|
125
|
+
attrs[:stime] = attr[14].to_i
|
126
|
+
attrs[:cpu_time] = (attrs[:utime] + attrs[:stime])
|
127
|
+
attrs[:cpu_percent] = cpu_percent({:cpu_time => attrs[:cpu_time], :proc_uptime => attr[21].to_i })
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get ctime of process from pid file timestamp
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# process_ctime(122)
|
136
|
+
#
|
137
|
+
# @param pid [Fixnum] process pid
|
138
|
+
# @return [aTime]
|
139
|
+
def process_ctime(pid)
|
140
|
+
pid_dir = "/proc/#{pid}"
|
141
|
+
(Dir.exist?(pid_dir)) ? File.ctime(pid_dir) : 0
|
142
|
+
end
|
143
|
+
|
144
|
+
# @note read access to cwd file are restricted only to owner of process
|
145
|
+
# Get current work directory
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# process_cwd(323)
|
149
|
+
#
|
150
|
+
# @param pid [Fixnum] process pid
|
151
|
+
# @return [String]
|
152
|
+
def process_cwd(pid)
|
153
|
+
cwd_file = "/proc/#{pid}/cwd"
|
154
|
+
(File.readable?(cwd_file)) ? File.readlink(cwd_file) : 'Permission denied'
|
155
|
+
end
|
156
|
+
|
157
|
+
# @note read access to io file are restricted only to owner of process
|
158
|
+
# Get command line arguments
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# process_cmdline(312)
|
162
|
+
#
|
163
|
+
# @param pid [Fixnum] process pid
|
164
|
+
# @return [String]
|
165
|
+
def process_cmdline(pid)
|
166
|
+
cmdline_file = "/proc/#{pid}/cmdline"
|
167
|
+
if File.readable? (cmdline_file)
|
168
|
+
File.foreach(cmdline_file).each do |line|
|
169
|
+
return line
|
170
|
+
end
|
171
|
+
else
|
172
|
+
'Permission denied'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Get soft and hard limits for process from limits file
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# p = process_limits('312')
|
180
|
+
#
|
181
|
+
# p.limits.each do |limit|
|
182
|
+
# puts "#{limit[name]} #{limit[:soft]} #{limit[:hard]}"
|
183
|
+
# end
|
184
|
+
#
|
185
|
+
# @param pid [Fixnum] process pid
|
186
|
+
# @return [Array<Hash>]
|
187
|
+
def process_limits(pid)
|
188
|
+
limits_file = "/proc/#{pid}/limits"
|
189
|
+
limits_list = Array.new
|
190
|
+
if File.readable?(limits_file)
|
191
|
+
File.foreach(limits_file).each do |line|
|
192
|
+
next if (line =~ /Limit/)
|
193
|
+
line_split = line.split(' ')
|
194
|
+
if line.split(' ')[1] == 'processes'
|
195
|
+
lname = "#{line_split[1]}"
|
196
|
+
lsoft = "#{line_split[2]} #{line_split[4]}"
|
197
|
+
lhard = "#{line_split[3]} #{line_split[4]}"
|
198
|
+
else
|
199
|
+
lname = "#{line_split[1]}_#{line_split[2]}"
|
200
|
+
if line.split(' ')[5]
|
201
|
+
lsoft = "#{line_split[3]} #{line_split[5]}"
|
202
|
+
lhard = "#{line_split[4]} #{line_split[5]}"
|
203
|
+
else
|
204
|
+
lsoft = "#{line_split[3]}"
|
205
|
+
lhard = "#{line_split[4]}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
limits_attrs = { :name => "#{lname}", :soft => "#{lsoft}", :hard => "#{lhard}" }
|
209
|
+
limits_list << limits_attrs
|
210
|
+
end
|
211
|
+
else
|
212
|
+
limits_list = ['Permission denied']
|
213
|
+
end
|
214
|
+
limits_list
|
215
|
+
end
|
216
|
+
|
217
|
+
# @note read access to fd directory are restricted only to owner of process
|
218
|
+
# Get & set open files and sockets from fd directory in attrs container
|
219
|
+
#
|
220
|
+
# @param attrs [Hash] hash list contains process attributes
|
221
|
+
# @param sockets [Array<Hash>] list sockets from /proc/net/tcp /proc/net/udp file
|
222
|
+
def process_files(attrs, sockets)
|
223
|
+
fd_dir = "/proc/#{attrs[:pid]}/fd"
|
224
|
+
files = Array.new
|
225
|
+
ports = Array.new
|
226
|
+
if File.readable?(fd_dir)
|
227
|
+
Dir.foreach(fd_dir).each do |fd|
|
228
|
+
if is_numeric?(fd)
|
229
|
+
file = File.readlink("#{fd_dir}/#{fd}")
|
230
|
+
attrs[:tty] = file if fd == '0'
|
231
|
+
if file =~ /^\// && file !~ /^\/(dev|proc)/
|
232
|
+
files << file
|
233
|
+
elsif file =~ /socket/
|
234
|
+
net_listen = compare_socket(file, sockets)
|
235
|
+
if net_listen; ports << net_listen end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
attrs[:open_files] = files
|
240
|
+
attrs[:listen_ports] = ports
|
241
|
+
else
|
242
|
+
attrs[:open_files] = 'Permission denied'
|
243
|
+
attrs[:listen_ports] = 'Permission denied'
|
244
|
+
attrs[:tty] = 'Permission denied'
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Match socket id from /proc/<pid>/fd with /proc/net/(tcp|udp)
|
249
|
+
#
|
250
|
+
# @return [String] containing matched protocol,ip and port (example: tcp:127.0.0.1:8080)
|
251
|
+
def compare_socket(file, sockets)
|
252
|
+
sockets.each do |socket|
|
253
|
+
return "#{socket[:protocol]}:#{socket[:address]}:#{socket[:port]}" if file =~ /#{socket[:id]}/
|
254
|
+
end
|
255
|
+
return nil
|
256
|
+
end
|
257
|
+
|
258
|
+
# @note read access to fd directory are restricted only to owner of process
|
259
|
+
# Get environment variables from environ file
|
260
|
+
#
|
261
|
+
# @example
|
262
|
+
# process_cmdline(312)
|
263
|
+
#
|
264
|
+
# @param pid [Fixnum] process pid
|
265
|
+
# @return [String]
|
266
|
+
def process_env(pid)
|
267
|
+
environ_file = "/proc/#{pid}/environ"
|
268
|
+
if File.readable? (environ_file)
|
269
|
+
File.foreach(environ_file).each do |line|
|
270
|
+
return line.split("\x0")
|
271
|
+
end
|
272
|
+
else
|
273
|
+
'Permission denied'
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Calculate %CPU usage per process
|
278
|
+
#
|
279
|
+
# @param attrs [Hash] hash list contains process attributes
|
280
|
+
# @option proc_uptime [Integer] - process uptime
|
281
|
+
# @option cpu_time [Integer] total cpu time spend in kernel and user mode
|
282
|
+
# @return [Float] average process cpu usage from start
|
283
|
+
def cpu_percent(attrs)
|
284
|
+
hertz = cpu_tck
|
285
|
+
sec = uptime - attrs[:proc_uptime] / hertz
|
286
|
+
if attrs[:cpu_time] > 0 && sec > 0
|
287
|
+
cpu = (attrs[:cpu_time] * 1000 / hertz) / sec
|
288
|
+
"#{cpu / 10}.#{cpu % 10}".to_f
|
289
|
+
else
|
290
|
+
return 0.0
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Check if object is numeric
|
295
|
+
#
|
296
|
+
# @example
|
297
|
+
# is_numeric?('2323')
|
298
|
+
#
|
299
|
+
# @return [Boolen]
|
300
|
+
def is_numeric?(obj)
|
301
|
+
obj.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
|
302
|
+
end
|
303
|
+
|
304
|
+
# Return the number of clock ticks
|
305
|
+
#
|
306
|
+
# @example
|
307
|
+
# cpu_tck()
|
308
|
+
#
|
309
|
+
# @return [Integer]
|
310
|
+
def cpu_tck
|
311
|
+
`getconf CLK_TCK`.to_i
|
312
|
+
rescue
|
313
|
+
return 100
|
314
|
+
end
|
315
|
+
|
316
|
+
# Get process uid and return username from passwd file
|
317
|
+
#
|
318
|
+
# @example
|
319
|
+
# process_username(132)
|
320
|
+
#
|
321
|
+
# @param pid [Fixnum] process pid
|
322
|
+
# @return [String]
|
323
|
+
def process_username(pid)
|
324
|
+
uid = File.stat("/proc/#{pid}").uid
|
325
|
+
File.foreach('/etc/passwd').each do |line|
|
326
|
+
if line.split(':')[2] == "#{uid}"
|
327
|
+
return line.split(':')[0]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Get list open tcp/upd ports from net/tcp and net/udp file and replace to decimal
|
333
|
+
#
|
334
|
+
# @example
|
335
|
+
# sockets = open_ports()
|
336
|
+
# sockets.each do |socket|
|
337
|
+
# puts "#{socket[:address]} #{socket[:port]}"
|
338
|
+
# end
|
339
|
+
#
|
340
|
+
# @return [Array<Hash>] list all used tcp/udp sockets
|
341
|
+
def open_ports
|
342
|
+
socket_list = Array.new
|
343
|
+
['tcp','udp'].each do |protocol|
|
344
|
+
File.foreach("/proc/net/#{protocol}").each do |line|
|
345
|
+
hex_port = line.split(' ')[1].split(':')[1]
|
346
|
+
hex_ip = line.split(' ')[1].split(':')[0].scan(/../)
|
347
|
+
socketid = line.split(' ')[9]
|
348
|
+
if hex_port =~ /$$$$/
|
349
|
+
hex_ip.map! { |e| e = e.to_i(16) }
|
350
|
+
socket_attrs = { :address => "#{hex_ip[3]}.#{hex_ip[2]}.#{hex_ip[1]}.#{hex_ip[0]}",
|
351
|
+
:port => hex_port.to_i(16),
|
352
|
+
:protocol => protocol,
|
353
|
+
:id => socketid }
|
354
|
+
socket_list << socket_attrs
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
return socket_list
|
359
|
+
end
|
360
|
+
|
361
|
+
# Return system uptime in second
|
362
|
+
#
|
363
|
+
# @example
|
364
|
+
# uptime()
|
365
|
+
#
|
366
|
+
# @return [Integer]
|
367
|
+
def uptime
|
368
|
+
File.foreach('/proc/uptime').each do |line|
|
369
|
+
return line.split[0].to_i
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Calculate percent of memory usage by process
|
374
|
+
#
|
375
|
+
# @example
|
376
|
+
# memory_percent(container,'')
|
377
|
+
#
|
378
|
+
# @param attrs [Hash] hash list contains process attributes
|
379
|
+
# @option :vmrss [Fixnum] rss memory allocated by process
|
380
|
+
# @param memtotal [Integer] total usable RAM
|
381
|
+
# @return [Float]
|
382
|
+
def memory_percent(attrs, memtotal)
|
383
|
+
if attrs[:vmrss]
|
384
|
+
return (attrs[:vmrss].to_f / memtotal.to_f * 100).round(2)
|
385
|
+
else
|
386
|
+
nil
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# Get total physical memory (RAM) size
|
391
|
+
#
|
392
|
+
# @example
|
393
|
+
# memory_total
|
394
|
+
#
|
395
|
+
# @return [Integer]
|
396
|
+
def memory_total
|
397
|
+
File.foreach('/proc/meminfo').each do |line|
|
398
|
+
return line.split(' ')[1].to_i if line =~ /MemTotal:/
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Linux
|
3
|
+
class ProcInfo < ProcControl
|
4
|
+
|
5
|
+
# Process instance with attributes of that process
|
6
|
+
# @param proc_attrs [Hash] attributes of the process
|
7
|
+
# @return [void]
|
8
|
+
def initialize(proc_attrs)
|
9
|
+
@proc_attrs = proc_attrs
|
10
|
+
define_attributes(proc_attrs)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Make attributes of public.
|
14
|
+
# Access to process attribute from an object instance by index of array.
|
15
|
+
# @example
|
16
|
+
# p = ProcInfo.new(proc_attrs)
|
17
|
+
# puts p['processid']
|
18
|
+
def [](key)
|
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
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Linux
|
3
|
+
class ProcTable
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Return attributes of searched process based on pid
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# search_by_pid(1761)
|
10
|
+
#
|
11
|
+
# @param [Integer] pid of process
|
12
|
+
# @return [ProcInfo] hash with process attributes
|
13
|
+
def search_by_pid(pid)
|
14
|
+
refresh
|
15
|
+
@proc_table.each do |process|
|
16
|
+
return process if process.pid.to_s == pid.to_s
|
17
|
+
end
|
18
|
+
return nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return attributes of searched process based on name or cmdline
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# search_by_name('java')
|
25
|
+
# search_by_name('/^regex/')
|
26
|
+
#
|
27
|
+
# @param process_name[String] name of process
|
28
|
+
# @return [Array<ProcInfo>] array of ProcInfo objects
|
29
|
+
def search_by_name(process_name)
|
30
|
+
refresh
|
31
|
+
process_list = Array.new
|
32
|
+
if process_name =~ /^\/.*\/$/
|
33
|
+
process_name.slice!(0)
|
34
|
+
process_name = Regexp.new(/#{process_name.chop}/)
|
35
|
+
end
|
36
|
+
@proc_table.each do |process|
|
37
|
+
if process_name.is_a? Regexp
|
38
|
+
process_list << process if process.name =~ process_name || process.cmdline =~ process_name
|
39
|
+
else
|
40
|
+
process_list << process if process.name == "#{process_name}" || process.cmdline == "#{process_name}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return process_list
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return attributes of searched process based on specified condition
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# search_by_condition(:attrs => 'vmsize', :oper => '<', value => '10000')
|
50
|
+
#
|
51
|
+
# @param args[Hash] attributes for search condition
|
52
|
+
# @option :attr [String], name of process attribute (first expression for if)
|
53
|
+
# @option :oper [String], operatator, available options: >,<,>=,<=,==,!=
|
54
|
+
# @option :value [String], value comparable (second expression for if)
|
55
|
+
#
|
56
|
+
# @return [Array<ProcInfo>] array of ProcInfo objects
|
57
|
+
def search_by_condition(args)
|
58
|
+
refresh
|
59
|
+
attr = args[:attr]
|
60
|
+
oper = args[:oper]
|
61
|
+
value = args[:value]
|
62
|
+
process_list = Array.new
|
63
|
+
@proc_table.each do |process|
|
64
|
+
value = value.to_i if process[:"#{attr}"].is_a? Fixnum
|
65
|
+
value = value.to_f if process[:"#{attr}"].is_a? Float
|
66
|
+
if oper == '>'
|
67
|
+
process_list << process if process[:"#{attr}"] > value if process[:"#{attr}"]
|
68
|
+
elsif oper == '<'
|
69
|
+
process_list << process if process[:"#{attr}"] < value if process[:"#{attr}"]
|
70
|
+
elsif oper == '>='
|
71
|
+
process_list << process if process[:"#{attr}"] >= value if process[:"#{attr}"]
|
72
|
+
elsif oper == '<='
|
73
|
+
process_list << process if process[:"#{attr}"] <= value if process[:"#{attr}"]
|
74
|
+
elsif oper == '=='
|
75
|
+
process_list << process if process[:"#{attr}"] == value if process[:"#{attr}"]
|
76
|
+
elsif oper == '!='
|
77
|
+
process_list << process if process[:"#{attr}"] != value if process[:"#{attr}"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return process_list
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return all process instances
|
84
|
+
#
|
85
|
+
# @example
|
86
|
+
# proc_table()
|
87
|
+
#
|
88
|
+
# @return [Array<ProcInfo>] array of ProcInfo objects
|
89
|
+
def proc_table
|
90
|
+
refresh
|
91
|
+
return @proc_table
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
# Refresh list of current running processes
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# refresh()
|
99
|
+
#
|
100
|
+
# @return [Array<ProcInfo>] array of ProcInfo objects
|
101
|
+
def refresh
|
102
|
+
@proc_table = Array.new
|
103
|
+
ProcFetch.get_process.each do |proc_attrs|
|
104
|
+
@proc_table.push(ProcInfo.new(proc_attrs))
|
105
|
+
end
|
106
|
+
childs_tree
|
107
|
+
end
|
108
|
+
|
109
|
+
# Find and add childs attribute for process
|
110
|
+
#
|
111
|
+
def childs_tree
|
112
|
+
@proc_table.each do |proc_parent|
|
113
|
+
@proc_table.each do |proc_child|
|
114
|
+
if proc_parent.pid == proc_child.ppid
|
115
|
+
proc_parent[:childs].push(proc_child)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
class HawatelPSException < Exception
|
3
|
+
# Custom exception
|
4
|
+
# @param args [Hash] the options to create a custom exception message
|
5
|
+
# @option :exception [Exception] Native Exception object
|
6
|
+
# @option :message [String] Custom message
|
7
|
+
# @return [void]
|
8
|
+
def initialize(args = {:exception => nil, :message => nil})
|
9
|
+
super(exception_enrichment(args))
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def exception_enrichment(args)
|
14
|
+
error_message = ''
|
15
|
+
error_message += args[:message] unless args[:message].nil?
|
16
|
+
error_message +=
|
17
|
+
"\nNative exception from '#{args[:exception].class}':\n#{args[:exception].message}" unless args[:exception].nil?
|
18
|
+
return error_message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module HawatelPS
|
2
|
+
module Windows
|
3
|
+
class ProcControl
|
4
|
+
|
5
|
+
# Check current process status
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# p = HawatelPS.search_by_name('notepad.exe')
|
9
|
+
# p.status
|
10
|
+
#
|
11
|
+
# @return [String] 'running' or 'not running'
|
12
|
+
def status
|
13
|
+
Process.kill 0, @proc_attrs[:processid].to_i
|
14
|
+
return "running"
|
15
|
+
rescue Errno::ESRCH
|
16
|
+
return "not running"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Suspend process without loss data
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# p = HawatelPS.search_by_pid('1020')
|
23
|
+
# p.suspend
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def suspend
|
27
|
+
# TODO There is no SuspendProcess API function in Windows.
|
28
|
+
# This was described in article: http://www.codeproject.com/Articles/2964/Win-process-suspend-resume-tool
|
29
|
+
# The suspend method isn't not save
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Resume suspended process
|
34
|
+
# @example
|
35
|
+
# p = HawatelPS.search_by_pid('1020')
|
36
|
+
# p.resume
|
37
|
+
# @return [String]
|
38
|
+
def resume
|
39
|
+
# TODO There is no ResumeProcess API function in Windows.
|
40
|
+
# This was described in article: http://www.codeproject.com/Articles/2964/Win-process-suspend-resume-tool
|
41
|
+
# The resume method isn't not save
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Terminate process
|
46
|
+
# @example
|
47
|
+
# p = HawatelPS.search_by_pid('1020')
|
48
|
+
# p.terminate
|
49
|
+
# @return [Integer] return source: https://msdn.microsoft.com/en-us/library/windows/desktop/aa393907
|
50
|
+
# * Successful completion (0)
|
51
|
+
# * Process not found (1)
|
52
|
+
# * Access denied (2)
|
53
|
+
# * Insufficient privilege (3)
|
54
|
+
# * Unknown failure (8)
|
55
|
+
# * Path not found (9)
|
56
|
+
# * Invalid parameter (21)
|
57
|
+
# * Other (22–4294967295)
|
58
|
+
def terminate
|
59
|
+
return @proc_attrs[:wmi_object].Terminate if @proc_attrs[:wmi_object].ole_respond_to?('Terminate')
|
60
|
+
rescue WIN32OLERuntimeError => ex
|
61
|
+
#raise HawatelPSException, :exception => ex, :message => "Cannot terminate process by WMI method Terminate()."
|
62
|
+
return 1
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|