epitools 0.5.106 → 0.5.107
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 +4 -4
- data/VERSION +1 -1
- data/lib/epitools/core_ext/enumerable.rb +3 -3
- data/lib/epitools/core_ext/string.rb +8 -0
- data/lib/epitools/path.rb +3 -1
- data/lib/epitools/sys.rb +4 -491
- data/lib/epitools/sys/mem.rb +38 -0
- data/lib/epitools/sys/misc.rb +60 -0
- data/lib/epitools/sys/mounts.rb +45 -0
- data/lib/epitools/sys/net.rb +85 -0
- data/lib/epitools/sys/os.rb +94 -0
- data/lib/epitools/sys/ps.rb +210 -0
- data/lib/epitools/wm.rb +1 -0
- data/spec/core_ext_spec.rb +2 -0
- data/spec/path_spec.rb +9 -0
- data/spec/sys_spec.rb +22 -39
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2acadccead525875804ab124cff3c74f5557c49
|
4
|
+
data.tar.gz: 1f845c9016e66d67fe845d5760ecb648396bf579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2091b4e7fa2c94c9b41da8ce0ba02b38f04fc5eeb7e40407ac5c8b2d5a0d7ad86c2a8be33109e9301fa8f75324114ae42058b3e266e9e11d7144789b2eaf948e
|
7
|
+
data.tar.gz: e96f90fa44265d05785d51fdd576c57d9ee792cd517d0f52f729cdce5c6dc794c849e74ace66196f561c9623f0e4adb5ffba9b42a38bf4f0fb826be0cf0065ee
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.107
|
@@ -190,7 +190,7 @@ module Enumerable
|
|
190
190
|
queue = Queue.new
|
191
191
|
each { |e| queue.push e }
|
192
192
|
|
193
|
-
Enumerator.new do |y|
|
193
|
+
Enumerator.new(queue.size) do |y|
|
194
194
|
workers = (0...num_workers).map do
|
195
195
|
Thread.new do
|
196
196
|
begin
|
@@ -569,7 +569,7 @@ class Enumerator
|
|
569
569
|
def +(other)
|
570
570
|
raise "Can only concatenate Enumerable things to Enumerators" unless Enumerable === other
|
571
571
|
|
572
|
-
Enumerator.new do |yielder|
|
572
|
+
Enumerator.new(count + other.count) do |yielder|
|
573
573
|
each { |e| yielder << e }
|
574
574
|
other.each { |e| yielder << e }
|
575
575
|
end
|
@@ -601,7 +601,7 @@ class Enumerator
|
|
601
601
|
# returning a new Enumerator. (The argument must be some kind of Enumerable.)
|
602
602
|
#
|
603
603
|
def cross_product(other)
|
604
|
-
Enumerator.new do |yielder|
|
604
|
+
Enumerator.new(count + other.count) do |yielder|
|
605
605
|
each { |a| other.each { |b| yielder << [a,b] } }
|
606
606
|
end
|
607
607
|
end
|
@@ -222,6 +222,14 @@ class String
|
|
222
222
|
URI.unescape(self)
|
223
223
|
end
|
224
224
|
|
225
|
+
#
|
226
|
+
# URI.parse the string and return an URI object
|
227
|
+
#
|
228
|
+
def to_uri
|
229
|
+
URI.parse self
|
230
|
+
end
|
231
|
+
alias_method :to_URI, :to_uri
|
232
|
+
|
225
233
|
#
|
226
234
|
# Convert a query string to a hash of params
|
227
235
|
#
|
data/lib/epitools/path.rb
CHANGED
data/lib/epitools/sys.rb
CHANGED
@@ -4,494 +4,7 @@ require 'epitools/minimal'
|
|
4
4
|
# Cross-platform operating system functions.
|
5
5
|
# Includes: process listing, platform detection, etc.
|
6
6
|
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
# Return the current operating system: Darwin, Linux, or Windows.
|
13
|
-
#
|
14
|
-
def self.os
|
15
|
-
return @os if @os
|
16
|
-
|
17
|
-
require 'rbconfig'
|
18
|
-
if defined? RbConfig
|
19
|
-
host_os = RbConfig::CONFIG['host_os']
|
20
|
-
else
|
21
|
-
host_os = Config::CONFIG['host_os']
|
22
|
-
end
|
23
|
-
|
24
|
-
case host_os
|
25
|
-
when /darwin/
|
26
|
-
@os = "Darwin"
|
27
|
-
when /bsd/
|
28
|
-
@os = "BSD"
|
29
|
-
when /linux/
|
30
|
-
@os = "Linux"
|
31
|
-
when /mingw|mswin|cygwin/
|
32
|
-
@os = 'Windows'
|
33
|
-
else
|
34
|
-
#raise "Unknown OS: #{host_os.inspect}"
|
35
|
-
end
|
36
|
-
|
37
|
-
@os
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
# Is this Linux?
|
42
|
-
#
|
43
|
-
def self.linux?
|
44
|
-
os == "Linux"
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# Is this Windows?
|
49
|
-
#
|
50
|
-
def self.windows?
|
51
|
-
os == "Windows"
|
52
|
-
end
|
53
|
-
|
54
|
-
#
|
55
|
-
# Is this Darwin?
|
56
|
-
#
|
57
|
-
def self.darwin?
|
58
|
-
os == "Darwin"
|
59
|
-
end
|
60
|
-
|
61
|
-
#
|
62
|
-
# Is this a Mac? (aka. Darwin?)
|
63
|
-
#
|
64
|
-
def self.mac?; darwin?; end
|
65
|
-
|
66
|
-
#
|
67
|
-
# Is this BSD?
|
68
|
-
#
|
69
|
-
def self.bsd?
|
70
|
-
os == "BSD" or os == "Darwin"
|
71
|
-
end
|
72
|
-
|
73
|
-
#-----------------------------------------------------------------------------
|
74
|
-
|
75
|
-
PS_FIELD_TABLE = [
|
76
|
-
[:pid, :to_i],
|
77
|
-
[:ppid, :to_i],
|
78
|
-
[:pcpu, :to_f],
|
79
|
-
[:pmem, :to_f],
|
80
|
-
[:stat, :to_s],
|
81
|
-
[:rss, :to_i],
|
82
|
-
[:vsz, :to_i],
|
83
|
-
[:user, :to_s],
|
84
|
-
[:majflt, :to_i],
|
85
|
-
[:minflt, :to_i],
|
86
|
-
[:command,:to_s],
|
87
|
-
]
|
88
|
-
|
89
|
-
PS_FIELDS = PS_FIELD_TABLE.map { |name, func| name }
|
90
|
-
PS_FIELD_TRANSFORMS = Hash[ *PS_FIELD_TABLE.flatten ]
|
91
|
-
|
92
|
-
class ProcessNotFound < Exception; end
|
93
|
-
|
94
|
-
#
|
95
|
-
# Contains all the information that PS can report about a process for
|
96
|
-
# the current platform.
|
97
|
-
#
|
98
|
-
# The following attribute accessor methods are available:
|
99
|
-
#
|
100
|
-
# pid (integer)
|
101
|
-
# command (string -- the 'ps' name)
|
102
|
-
# name (alias for 'command')
|
103
|
-
# pcpu (float)
|
104
|
-
# pmem (float)
|
105
|
-
# stat (string)
|
106
|
-
# rss (integer)
|
107
|
-
# vsz (integer)
|
108
|
-
# user (string)
|
109
|
-
# majflt (integer)
|
110
|
-
# minflt (integer)
|
111
|
-
# state (array of symbols; see DARWIN_STATES or LINUX_STATES)
|
112
|
-
#
|
113
|
-
# Only on linux:
|
114
|
-
# exename (string -- path to the binary)
|
115
|
-
# fds (array -- list of open file descriptors)
|
116
|
-
#
|
117
|
-
class ProcessInfo < Struct.new(*PS_FIELDS+[:state])
|
118
|
-
|
119
|
-
DARWIN_STATES = {
|
120
|
-
"R"=>:running,
|
121
|
-
"S"=>:sleeping,
|
122
|
-
"I"=>:idle,
|
123
|
-
"T"=>:stopped,
|
124
|
-
"U"=>:wait,
|
125
|
-
"Z"=>:zombie,
|
126
|
-
"W"=>:swapped,
|
127
|
-
|
128
|
-
"s"=>:session_leader,
|
129
|
-
"X"=>:debugging,
|
130
|
-
"E"=>:exiting,
|
131
|
-
"<"=>:high_priority,
|
132
|
-
"N"=>:low_priority,
|
133
|
-
"+"=>:foreground,
|
134
|
-
"L"=>:locked_pages,
|
135
|
-
}
|
136
|
-
|
137
|
-
LINUX_STATES = {
|
138
|
-
"R"=>:running,
|
139
|
-
"S"=>:sleeping,
|
140
|
-
"T"=>:stopped,
|
141
|
-
"D"=>:wait,
|
142
|
-
"Z"=>:zombie,
|
143
|
-
"W"=>:swapped,
|
144
|
-
"X"=>:dead,
|
145
|
-
|
146
|
-
"s"=>:session_leader,
|
147
|
-
"<"=>:high_priority,
|
148
|
-
"N"=>:low_priority,
|
149
|
-
"+"=>:foreground,
|
150
|
-
"L"=>:locked_pages,
|
151
|
-
"l"=>:multithreaded,
|
152
|
-
}
|
153
|
-
|
154
|
-
def initialize(*args)
|
155
|
-
@dead = false
|
156
|
-
args << stat_to_state(args[PS_FIELDS.index(:stat)])
|
157
|
-
super(*args)
|
158
|
-
end
|
159
|
-
|
160
|
-
def parent
|
161
|
-
Sys.ps(ppid).first unless ppid < 1
|
162
|
-
end
|
163
|
-
|
164
|
-
def children
|
165
|
-
@@parents ||= Sys.ps.group_by(&:ppid)
|
166
|
-
@@parents[pid]
|
167
|
-
end
|
168
|
-
|
169
|
-
#
|
170
|
-
# Convert all the process information to a hash.
|
171
|
-
#
|
172
|
-
def to_hash
|
173
|
-
Hash[ *members.zip(values).flatten(1) ]
|
174
|
-
end
|
175
|
-
|
176
|
-
#
|
177
|
-
# Send the TERM signal to this process.
|
178
|
-
#
|
179
|
-
def kill!(signal="TERM")
|
180
|
-
puts "Killing #{pid} (#{signal})"
|
181
|
-
Process.kill(signal, pid)
|
182
|
-
# TODO: handle exception Errno::ESRCH (no such process)
|
183
|
-
end
|
184
|
-
|
185
|
-
#
|
186
|
-
# Has this process been killed?
|
187
|
-
#
|
188
|
-
def dead?
|
189
|
-
@dead ||= Sys.pid(pid).empty?
|
190
|
-
end
|
191
|
-
|
192
|
-
#
|
193
|
-
# Refresh this process' statistics.
|
194
|
-
#
|
195
|
-
def refresh
|
196
|
-
processes = Sys.ps(pid)
|
197
|
-
|
198
|
-
if processes.empty?
|
199
|
-
@dead = true
|
200
|
-
raise ProcessNotFound
|
201
|
-
end
|
202
|
-
|
203
|
-
updated_process = processes.first
|
204
|
-
members.each { |member| self[member] = updated_process[member] }
|
205
|
-
self
|
206
|
-
end
|
207
|
-
|
208
|
-
alias_method :name, :command
|
209
|
-
|
210
|
-
# Linux-specific methods
|
211
|
-
if Sys.linux?
|
212
|
-
|
213
|
-
def exename
|
214
|
-
@exename ||= File.readlink("/proc/#{pid}/exe") rescue :unknown
|
215
|
-
@exename == :unknown ? nil : @exename
|
216
|
-
end
|
217
|
-
|
218
|
-
def fds
|
219
|
-
Dir["/proc/#{pid}/fd/*"].map { |fd| File.readlink(fd) rescue nil }
|
220
|
-
end
|
221
|
-
|
222
|
-
end
|
223
|
-
|
224
|
-
private
|
225
|
-
|
226
|
-
def stat_to_state(str)
|
227
|
-
states = case Sys.os
|
228
|
-
when "Linux" then LINUX_STATES
|
229
|
-
when "Darwin" then DARWIN_STATES
|
230
|
-
else raise "Unsupported platform: #{Sys.os}"
|
231
|
-
end
|
232
|
-
|
233
|
-
str.scan(/./).map { |char| states[char] }.compact
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
#-----------------------------------------------------------------------------
|
238
|
-
|
239
|
-
def self.tree
|
240
|
-
tree = Sys.ps.group_by(&:ppid)
|
241
|
-
Hash[tree.map do |ppid, children|
|
242
|
-
kvs = children.map { |child| [child.pid, tree.delete(child.pid)] }
|
243
|
-
[ppid, Hash[kvs]]
|
244
|
-
end]
|
245
|
-
end
|
246
|
-
|
247
|
-
#
|
248
|
-
# List all (or specified) processes, and return ProcessInfo objects.
|
249
|
-
# (Takes an optional list of pids as arguments.)
|
250
|
-
#
|
251
|
-
def self.ps(*pids)
|
252
|
-
#return @@cache if @@cache
|
253
|
-
|
254
|
-
options = PS_FIELDS.join(',')
|
255
|
-
|
256
|
-
pids = pids.map(&:to_i)
|
257
|
-
|
258
|
-
if pids.any?
|
259
|
-
command = "ps -p #{pids.join(',')} -o #{options}"
|
260
|
-
else
|
261
|
-
command = "ps awx -o #{options}"
|
262
|
-
end
|
263
|
-
|
264
|
-
lines = `#{command}`.lines.to_a
|
265
|
-
|
266
|
-
lines[1..-1].map do |line|
|
267
|
-
fields = line.split
|
268
|
-
if fields.size > PS_FIELDS.size
|
269
|
-
fields = fields[0..PS_FIELDS.size-2] + [fields[PS_FIELDS.size-1..-1].join(" ")]
|
270
|
-
end
|
271
|
-
|
272
|
-
fields = PS_FIELDS.zip(fields).map { |name, value| value.send(PS_FIELD_TRANSFORMS[name]) }
|
273
|
-
|
274
|
-
ProcessInfo.new(*fields)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
#-----------------------------------------------------------------------------
|
279
|
-
|
280
|
-
def self.refresh
|
281
|
-
@@cache = nil
|
282
|
-
end
|
283
|
-
|
284
|
-
#
|
285
|
-
# Trap signals!
|
286
|
-
#
|
287
|
-
# usage: trap("EXIT", "HUP", "ETC", :ignore=>["VTALRM"]) { |signal| puts "Got #{signal}!" }
|
288
|
-
# (Execute Signal.list to see what's available.)
|
289
|
-
#
|
290
|
-
# No paramters defaults to all signals except VTALRM, CHLD, CLD, and EXIT.
|
291
|
-
#
|
292
|
-
def self.trap(*args, &block)
|
293
|
-
options = if args.last.is_a?(Hash) then args.pop else Hash.new end
|
294
|
-
args = [args].flatten
|
295
|
-
signals = if args.any? then args else Signal.list.keys end
|
296
|
-
|
297
|
-
ignore = %w[ VTALRM CHLD CLD EXIT ] unless ignore = options[:ignore]
|
298
|
-
ignore = [ignore] unless ignore.is_a? Array
|
299
|
-
|
300
|
-
signals = signals - ignore
|
301
|
-
|
302
|
-
signals.each do |signal|
|
303
|
-
p [:sig, signal]
|
304
|
-
Signal.trap(signal) { yield signal }
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
#-----------------------------------------------------------------------------
|
309
|
-
|
310
|
-
#
|
311
|
-
# A metaprogramming helper that allows you to write platform-specific methods
|
312
|
-
# which the user can call with one name. Here's how to use it:
|
313
|
-
#
|
314
|
-
# Define these methods:
|
315
|
-
# reboot_linux, reboot_darwin, reboot_windows
|
316
|
-
#
|
317
|
-
# Call the magic method:
|
318
|
-
# cross_platform_method(:reboot)
|
319
|
-
#
|
320
|
-
# Now the user can execute "reboot" on any platform!
|
321
|
-
#
|
322
|
-
# (Note: If you didn't create a method for a specific platform, then you'll get
|
323
|
-
# NoMethodError exception when the "reboot" method is called on that platform.)
|
324
|
-
#
|
325
|
-
def self.cross_platform_method(name)
|
326
|
-
platform_method_name = "#{name}_#{os.downcase}"
|
327
|
-
metaclass.instance_eval do
|
328
|
-
define_method(name) do |*args|
|
329
|
-
begin
|
330
|
-
self.send(platform_method_name, *args)
|
331
|
-
rescue NoMethodError
|
332
|
-
raise NotImplementedError.new("#{name} is not yet supported on #{os}.")
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
#-----------------------------------------------------------------------------
|
339
|
-
|
340
|
-
cross_platform_method :hostname
|
341
|
-
|
342
|
-
def self.hostname_linux
|
343
|
-
`uname -n`.strip
|
344
|
-
end
|
345
|
-
|
346
|
-
def self.hostname_mac
|
347
|
-
`uname -n`.strip.gsub(/\.local$/, '')
|
348
|
-
end
|
349
|
-
|
350
|
-
def self.hostname_windows
|
351
|
-
raise NotImplementedError
|
352
|
-
end
|
353
|
-
|
354
|
-
#-----------------------------------------------------------------------------
|
355
|
-
|
356
|
-
cross_platform_method :interfaces
|
357
|
-
|
358
|
-
#
|
359
|
-
# Darwin: Return a hash of (device, IP address) pairs.
|
360
|
-
#
|
361
|
-
# eg: {"en0"=>"192.168.1.101"}
|
362
|
-
#
|
363
|
-
def self.interfaces_bsd
|
364
|
-
sections = `ifconfig`.split(/^(?=[^\t])/)
|
365
|
-
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
366
|
-
|
367
|
-
device_ips = {}
|
368
|
-
sections_with_relevant_ip.each do |section|
|
369
|
-
device = section[/[^:]+/]
|
370
|
-
ip = section[/inet ([^ ]+)/, 1]
|
371
|
-
device_ips[device] = ip
|
372
|
-
end
|
373
|
-
|
374
|
-
device_ips
|
375
|
-
end
|
376
|
-
|
377
|
-
def self.interfaces_darwin; interfaces_bsd; end
|
378
|
-
|
379
|
-
#
|
380
|
-
# Linux: Return a hash of (device, IP address) pairs.
|
381
|
-
#
|
382
|
-
# eg: {"eth0"=>"192.168.1.101"}
|
383
|
-
#
|
384
|
-
def self.interfaces_linux
|
385
|
-
sections = `/sbin/ifconfig`.split(/^(?=Link encap:Ethernet)/)
|
386
|
-
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
387
|
-
|
388
|
-
device_ips = {}
|
389
|
-
sections_with_relevant_ip.each do |section|
|
390
|
-
device = section[/([\w\d]+)\s+Link encap:Ethernet/, 1]
|
391
|
-
ip = section[/inet addr:([^\s]+)/, 1]
|
392
|
-
device_ips[device] = ip
|
393
|
-
end
|
394
|
-
|
395
|
-
device_ips
|
396
|
-
end
|
397
|
-
|
398
|
-
#
|
399
|
-
# Windows: Return a hash of (device name, IP address) pairs.
|
400
|
-
#
|
401
|
-
def self.interfaces_windows
|
402
|
-
result = {}
|
403
|
-
`ipconfig`.split_before(/^\w.+:/).each do |chunk|
|
404
|
-
chunk.grep(/^Ethernet adapter (.+):\s*$/) do
|
405
|
-
name = $1
|
406
|
-
chunk.grep(/IPv[46] Address[\.\ ]+: (.+)$/) do
|
407
|
-
address = $1.strip
|
408
|
-
result[name] = address
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
result
|
413
|
-
end
|
414
|
-
|
415
|
-
#-----------------------------------------------------------------------------
|
416
|
-
|
417
|
-
cross_platform_method :browser_open
|
418
|
-
|
419
|
-
#
|
420
|
-
# Linux: Open an URL in the default browser (using "gnome-open").
|
421
|
-
#
|
422
|
-
def browser_open_linux(url)
|
423
|
-
system("gnome-open", url)
|
424
|
-
end
|
425
|
-
|
426
|
-
#
|
427
|
-
# Darwin: Open the webpage in a new chrome tab.
|
428
|
-
#
|
429
|
-
def browser_open_darwin(url)
|
430
|
-
system("open", "-a", "chrome", url)
|
431
|
-
end
|
432
|
-
|
433
|
-
#-----------------------------------------------------------------------------
|
434
|
-
|
435
|
-
cross_platform_method :memstat
|
436
|
-
|
437
|
-
def self.memstat_linux
|
438
|
-
#$ free
|
439
|
-
# total used free shared buffers cached
|
440
|
-
#Mem: 4124380 3388548 735832 0 561888 968004
|
441
|
-
#-/+ buffers/cache: 1858656 2265724
|
442
|
-
#Swap: 2104504 166724 1937780
|
443
|
-
|
444
|
-
#$ vmstat
|
445
|
-
raise "Not implemented"
|
446
|
-
end
|
447
|
-
|
448
|
-
def self.memstat_darwin
|
449
|
-
#$ vm_stat
|
450
|
-
#Mach Virtual Memory Statistics: (page size of 4096 bytes)
|
451
|
-
#Pages free: 198367.
|
452
|
-
#Pages active: 109319.
|
453
|
-
#Pages inactive: 61946.
|
454
|
-
#Pages speculative: 18674.
|
455
|
-
#Pages wired down: 70207.
|
456
|
-
#"Translation faults": 158788687.
|
457
|
-
#Pages copy-on-write: 17206973.
|
458
|
-
#Pages zero filled: 54584525.
|
459
|
-
#Pages reactivated: 8768.
|
460
|
-
#Pageins: 176076.
|
461
|
-
#Pageouts: 3757.
|
462
|
-
#Object cache: 16 hits of 255782 lookups (0% hit rate)
|
463
|
-
|
464
|
-
#$ iostat
|
465
|
-
raise "Not implemented"
|
466
|
-
end
|
467
|
-
|
468
|
-
#-----------------------------------------------------------------------------
|
469
|
-
|
470
|
-
def self.temperatures
|
471
|
-
#/Applications/Utilities/TemperatureMonitor.app/Contents/MacOS/tempmonitor -a -l
|
472
|
-
#CPU Core 1: 28 C
|
473
|
-
#CPU Core 2: 28 C
|
474
|
-
#SMART Disk Hitachi HTS543216L9SA02 (090831FBE200VCGH3D5F): 40 C
|
475
|
-
#SMC CPU A DIODE: 41 C
|
476
|
-
#SMC CPU A HEAT SINK: 42 C
|
477
|
-
#SMC DRIVE BAY 1: 41 C
|
478
|
-
#SMC NORTHBRIDGE POS 1: 46 C
|
479
|
-
#SMC WLAN CARD: 45 C
|
480
|
-
raise "Not implemented"
|
481
|
-
end
|
482
|
-
|
483
|
-
end
|
484
|
-
|
485
|
-
if $0 == __FILE__
|
486
|
-
require 'pp'
|
487
|
-
procs = Sys.ps
|
488
|
-
p [:processes, procs.size]
|
489
|
-
# some = procs[0..3]
|
490
|
-
# some.each{|ps| pp ps}
|
491
|
-
# some.first.kill!
|
492
|
-
# pp some.first.to_hash
|
493
|
-
# p [:total_cpu, procs.map{|ps| ps.pcpu}.sum]
|
494
|
-
# p [:total_mem, procs.map{|ps| ps.pmem}.sum]
|
495
|
-
|
496
|
-
pp Sys.interfaces
|
497
|
-
end
|
7
|
+
require 'epitools/sys/ps'
|
8
|
+
require 'epitools/sys/mounts'
|
9
|
+
require 'epitools/sys/misc'
|
10
|
+
require 'epitools/sys/net'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Sys
|
2
|
+
|
3
|
+
#-----------------------------------------------------------------------------
|
4
|
+
|
5
|
+
cross_platform_method :memstat
|
6
|
+
|
7
|
+
def self.memstat_linux
|
8
|
+
#$ free
|
9
|
+
# total used free shared buffers cached
|
10
|
+
#Mem: 4124380 3388548 735832 0 561888 968004
|
11
|
+
#-/+ buffers/cache: 1858656 2265724
|
12
|
+
#Swap: 2104504 166724 1937780
|
13
|
+
|
14
|
+
#$ vmstat
|
15
|
+
raise "Not implemented"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.memstat_darwin
|
19
|
+
#$ vm_stat
|
20
|
+
#Mach Virtual Memory Statistics: (page size of 4096 bytes)
|
21
|
+
#Pages free: 198367.
|
22
|
+
#Pages active: 109319.
|
23
|
+
#Pages inactive: 61946.
|
24
|
+
#Pages speculative: 18674.
|
25
|
+
#Pages wired down: 70207.
|
26
|
+
#"Translation faults": 158788687.
|
27
|
+
#Pages copy-on-write: 17206973.
|
28
|
+
#Pages zero filled: 54584525.
|
29
|
+
#Pages reactivated: 8768.
|
30
|
+
#Pageins: 176076.
|
31
|
+
#Pageouts: 3757.
|
32
|
+
#Object cache: 16 hits of 255782 lookups (0% hit rate)
|
33
|
+
|
34
|
+
#$ iostat
|
35
|
+
raise "Not implemented"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Sys
|
2
|
+
|
3
|
+
#
|
4
|
+
# Trap signals!
|
5
|
+
#
|
6
|
+
# usage: trap("EXIT", "HUP", "ETC", :ignore=>["VTALRM"]) { |signal| puts "Got #{signal}!" }
|
7
|
+
# (Execute Signal.list to see what's available.)
|
8
|
+
#
|
9
|
+
# No paramters defaults to all signals except VTALRM, CHLD, CLD, and EXIT.
|
10
|
+
#
|
11
|
+
def self.trap(*args, &block)
|
12
|
+
options = if args.last.is_a?(Hash) then args.pop else Hash.new end
|
13
|
+
args = [args].flatten
|
14
|
+
signals = if args.any? then args else Signal.list.keys end
|
15
|
+
|
16
|
+
ignore = %w[ VTALRM CHLD CLD EXIT ] unless ignore = options[:ignore]
|
17
|
+
ignore = [ignore] unless ignore.is_a? Array
|
18
|
+
|
19
|
+
signals = signals - ignore
|
20
|
+
|
21
|
+
signals.each do |signal|
|
22
|
+
p [:sig, signal]
|
23
|
+
Signal.trap(signal) { yield signal }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#-----------------------------------------------------------------------------
|
28
|
+
|
29
|
+
cross_platform_method :browser_open
|
30
|
+
|
31
|
+
#
|
32
|
+
# Linux: Open an URL in the default browser (using "xdg-open").
|
33
|
+
#
|
34
|
+
def browser_open_linux(url)
|
35
|
+
system("xdg-open", url)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Darwin: Open the webpage in a new chrome tab.
|
40
|
+
#
|
41
|
+
def browser_open_darwin(url)
|
42
|
+
system("open", "-a", "chrome", url)
|
43
|
+
end
|
44
|
+
|
45
|
+
#-----------------------------------------------------------------------------
|
46
|
+
|
47
|
+
def self.temperatures
|
48
|
+
#/Applications/Utilities/TemperatureMonitor.app/Contents/MacOS/tempmonitor -a -l
|
49
|
+
#CPU Core 1: 28 C
|
50
|
+
#CPU Core 2: 28 C
|
51
|
+
#SMART Disk Hitachi HTS543216L9SA02 (090831FBE200VCGH3D5F): 40 C
|
52
|
+
#SMC CPU A DIODE: 41 C
|
53
|
+
#SMC CPU A HEAT SINK: 42 C
|
54
|
+
#SMC DRIVE BAY 1: 41 C
|
55
|
+
#SMC NORTHBRIDGE POS 1: 46 C
|
56
|
+
#SMC WLAN CARD: 45 C
|
57
|
+
raise NotImplementedError.new("Sorry")
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Sys
|
2
|
+
|
3
|
+
#
|
4
|
+
# Get an array of mounted filesystems (as fancy objects)
|
5
|
+
#
|
6
|
+
def self.mounts
|
7
|
+
if linux?
|
8
|
+
IO.popen(["findmnt", "--raw"]) { |io| io.drop(1).map { |line| Mount.new line } }
|
9
|
+
else
|
10
|
+
raise NotImplementedError.new("I dunno, how do you find mounts on #{os}?")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
class Mount
|
16
|
+
attr_accessor :dev, :type, :options
|
17
|
+
|
18
|
+
def initialize(line)
|
19
|
+
@path, @dev, @type, @options = line.strip.split(' ')
|
20
|
+
@options = @options.split(",")
|
21
|
+
end
|
22
|
+
|
23
|
+
def system?
|
24
|
+
(path =~ %r{^/(sys|dev|proc|run/user|tmp)}) or dev == "systemd-1"
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"#{type}: #{path} (#{dev})"
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"#{path} (#{dev})"
|
33
|
+
end
|
34
|
+
|
35
|
+
def path
|
36
|
+
# Unescape findmnt's hex codes
|
37
|
+
Path.new "#{eval %{"#{@path}"}}/"
|
38
|
+
end
|
39
|
+
|
40
|
+
def dirname
|
41
|
+
path.dirs.last
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Sys
|
2
|
+
|
3
|
+
#-----------------------------------------------------------------------------
|
4
|
+
|
5
|
+
cross_platform_method :hostname
|
6
|
+
|
7
|
+
def self.hostname_linux
|
8
|
+
`uname -n`.strip
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.hostname_mac
|
12
|
+
`uname -n`.strip.gsub(/\.local$/, '')
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.hostname_windows
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
#-----------------------------------------------------------------------------
|
20
|
+
|
21
|
+
cross_platform_method :interfaces
|
22
|
+
|
23
|
+
#
|
24
|
+
# BSD: Return a hash of (device, IP address) pairs.
|
25
|
+
#
|
26
|
+
# eg: {"en0"=>"192.168.1.101"}
|
27
|
+
#
|
28
|
+
def self.interfaces_bsd
|
29
|
+
sections = `ifconfig`.split(/^(?=[^\t])/)
|
30
|
+
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
31
|
+
|
32
|
+
device_ips = {}
|
33
|
+
sections_with_relevant_ip.each do |section|
|
34
|
+
device = section[/[^:]+/]
|
35
|
+
ip = section[/inet ([^ ]+)/, 1]
|
36
|
+
device_ips[device] = ip
|
37
|
+
end
|
38
|
+
|
39
|
+
device_ips
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Darwin: Do whatever BSD does
|
44
|
+
#
|
45
|
+
def self.interfaces_darwin
|
46
|
+
interfaces_bsd
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Linux: Return a hash of (device, IP address) pairs.
|
51
|
+
#
|
52
|
+
# eg: {"eth0"=>"192.168.1.101"}
|
53
|
+
#
|
54
|
+
def self.interfaces_linux
|
55
|
+
sections = `/sbin/ifconfig`.split(/^(?=Link encap:Ethernet)/)
|
56
|
+
sections_with_relevant_ip = sections.select {|i| i =~ /inet/ }
|
57
|
+
|
58
|
+
device_ips = {}
|
59
|
+
sections_with_relevant_ip.each do |section|
|
60
|
+
device = section[/([\w\d]+)\s+Link encap:Ethernet/, 1]
|
61
|
+
ip = section[/inet addr:([^\s]+)/, 1]
|
62
|
+
device_ips[device] = ip
|
63
|
+
end
|
64
|
+
|
65
|
+
device_ips
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Windows: Return a hash of (device name, IP address) pairs.
|
70
|
+
#
|
71
|
+
def self.interfaces_windows
|
72
|
+
result = {}
|
73
|
+
`ipconfig`.split_before(/^\w.+:/).each do |chunk|
|
74
|
+
chunk.grep(/^Ethernet adapter (.+):\s*$/) do
|
75
|
+
name = $1
|
76
|
+
chunk.grep(/IPv[46] Address[\.\ ]+: (.+)$/) do
|
77
|
+
address = $1.strip
|
78
|
+
result[name] = address
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Sys
|
2
|
+
|
3
|
+
#
|
4
|
+
# Return the current operating system: Darwin, Linux, or Windows.
|
5
|
+
#
|
6
|
+
def self.os
|
7
|
+
return @os if @os
|
8
|
+
|
9
|
+
require 'rbconfig'
|
10
|
+
if defined? RbConfig
|
11
|
+
host_os = RbConfig::CONFIG['host_os']
|
12
|
+
else
|
13
|
+
host_os = Config::CONFIG['host_os']
|
14
|
+
end
|
15
|
+
|
16
|
+
case host_os
|
17
|
+
when /darwin/
|
18
|
+
@os = "Darwin"
|
19
|
+
when /bsd/
|
20
|
+
@os = "BSD"
|
21
|
+
when /linux/
|
22
|
+
@os = "Linux"
|
23
|
+
when /mingw|mswin|cygwin/
|
24
|
+
@os = 'Windows'
|
25
|
+
else
|
26
|
+
#raise "Unknown OS: #{host_os.inspect}"
|
27
|
+
end
|
28
|
+
|
29
|
+
@os
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Is this Linux?
|
34
|
+
#
|
35
|
+
def self.linux?
|
36
|
+
os == "Linux"
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Is this Windows?
|
41
|
+
#
|
42
|
+
def self.windows?
|
43
|
+
os == "Windows"
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Is this Darwin?
|
48
|
+
#
|
49
|
+
def self.darwin?
|
50
|
+
os == "Darwin"
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Is this a Mac? (aka. Darwin?)
|
55
|
+
#
|
56
|
+
def self.mac?; darwin?; end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Is this BSD?
|
60
|
+
#
|
61
|
+
def self.bsd?
|
62
|
+
os == "BSD" or os == "Darwin"
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
#
|
67
|
+
# A metaprogramming helper that allows you to write platform-specific methods
|
68
|
+
# which the user can call with one name. Here's how to use it:
|
69
|
+
#
|
70
|
+
# Define these methods:
|
71
|
+
# reboot_linux, reboot_darwin, reboot_windows
|
72
|
+
#
|
73
|
+
# Call the magic method:
|
74
|
+
# cross_platform_method(:reboot)
|
75
|
+
#
|
76
|
+
# Now the user can execute "reboot" on any platform!
|
77
|
+
#
|
78
|
+
# (Note: If you didn't create a method for a specific platform, then you'll get
|
79
|
+
# NoMethodError exception when the "reboot" method is called on that platform.)
|
80
|
+
#
|
81
|
+
def self.cross_platform_method(name)
|
82
|
+
platform_method_name = "#{name}_#{os.downcase}"
|
83
|
+
metaclass.instance_eval do
|
84
|
+
define_method(name) do |*args|
|
85
|
+
begin
|
86
|
+
self.send(platform_method_name, *args)
|
87
|
+
rescue NoMethodError
|
88
|
+
raise NotImplementedError.new("#{name} is not yet supported on #{os}.")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require_relative "os"
|
2
|
+
|
3
|
+
module Sys
|
4
|
+
|
5
|
+
#
|
6
|
+
# List all (or specified) processes, and return ProcessInfo objects.
|
7
|
+
# (Takes an optional list of pids as arguments.)
|
8
|
+
#
|
9
|
+
def self.ps(*pids)
|
10
|
+
raise "that's too many pids!" if pids.size > 999_999
|
11
|
+
|
12
|
+
options = PS_FIELDS.join(',')
|
13
|
+
|
14
|
+
pids = pids.map(&:to_i)
|
15
|
+
|
16
|
+
if pids.any?
|
17
|
+
command = "ps -p #{pids.join(',')} -o #{options}"
|
18
|
+
else
|
19
|
+
command = "ps awx -o #{options}"
|
20
|
+
end
|
21
|
+
|
22
|
+
lines = `#{command}`.lines.to_a
|
23
|
+
|
24
|
+
lines[1..-1].map do |line|
|
25
|
+
fields = line.split
|
26
|
+
if fields.size > PS_FIELDS.size
|
27
|
+
fields = fields[0..PS_FIELDS.size-2] + [fields[PS_FIELDS.size-1..-1].join(" ")]
|
28
|
+
end
|
29
|
+
|
30
|
+
fields = PS_FIELDS.zip(fields).map { |name, value| value.send(PS_FIELD_TRANSFORMS[name]) }
|
31
|
+
|
32
|
+
ProcessInfo.new(*fields)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#-----------------------------------------------------------------------------
|
37
|
+
|
38
|
+
def self.tree
|
39
|
+
tree = Sys.ps.group_by(&:ppid)
|
40
|
+
Hash[tree.map do |ppid, children|
|
41
|
+
kvs = children.map { |child| [child.pid, tree.delete(child.pid)] }
|
42
|
+
[ppid, Hash[kvs]]
|
43
|
+
end]
|
44
|
+
end
|
45
|
+
|
46
|
+
#-----------------------------------------------------------------------------
|
47
|
+
|
48
|
+
PS_FIELD_TABLE = [
|
49
|
+
[:pid, :to_i],
|
50
|
+
[:ppid, :to_i],
|
51
|
+
[:pcpu, :to_f],
|
52
|
+
[:pmem, :to_f],
|
53
|
+
[:stat, :to_s],
|
54
|
+
[:rss, :to_i],
|
55
|
+
[:vsz, :to_i],
|
56
|
+
[:user, :to_s],
|
57
|
+
[:majflt, :to_i],
|
58
|
+
[:minflt, :to_i],
|
59
|
+
[:command,:to_s],
|
60
|
+
]
|
61
|
+
|
62
|
+
PS_FIELDS = PS_FIELD_TABLE.map { |name, func| name }
|
63
|
+
PS_FIELD_TRANSFORMS = Hash[ *PS_FIELD_TABLE.flatten ]
|
64
|
+
|
65
|
+
class ProcessNotFound < Exception; end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Contains all the information that PS can report about a process for
|
69
|
+
# the current platform.
|
70
|
+
#
|
71
|
+
# The following attribute accessor methods are available:
|
72
|
+
#
|
73
|
+
# pid (integer)
|
74
|
+
# command (string -- the 'ps' name)
|
75
|
+
# name (alias for 'command')
|
76
|
+
# pcpu (float)
|
77
|
+
# pmem (float)
|
78
|
+
# stat (string)
|
79
|
+
# rss (integer)
|
80
|
+
# vsz (integer)
|
81
|
+
# user (string)
|
82
|
+
# majflt (integer)
|
83
|
+
# minflt (integer)
|
84
|
+
# state (array of symbols; see DARWIN_STATES or LINUX_STATES)
|
85
|
+
#
|
86
|
+
# Only on linux:
|
87
|
+
# exename (string -- path to the binary)
|
88
|
+
# fds (array -- list of open file descriptors)
|
89
|
+
#
|
90
|
+
class ProcessInfo < Struct.new(*PS_FIELDS+[:state])
|
91
|
+
|
92
|
+
DARWIN_STATES = {
|
93
|
+
"R"=>:running,
|
94
|
+
"S"=>:sleeping,
|
95
|
+
"I"=>:idle,
|
96
|
+
"T"=>:stopped,
|
97
|
+
"U"=>:wait,
|
98
|
+
"Z"=>:zombie,
|
99
|
+
"W"=>:swapped,
|
100
|
+
|
101
|
+
"s"=>:session_leader,
|
102
|
+
"X"=>:debugging,
|
103
|
+
"E"=>:exiting,
|
104
|
+
"<"=>:high_priority,
|
105
|
+
"N"=>:low_priority,
|
106
|
+
"+"=>:foreground,
|
107
|
+
"L"=>:locked_pages,
|
108
|
+
}
|
109
|
+
|
110
|
+
LINUX_STATES = {
|
111
|
+
"R"=>:running,
|
112
|
+
"S"=>:sleeping,
|
113
|
+
"T"=>:stopped,
|
114
|
+
"D"=>:wait,
|
115
|
+
"Z"=>:zombie,
|
116
|
+
"W"=>:swapped,
|
117
|
+
"X"=>:dead,
|
118
|
+
|
119
|
+
"s"=>:session_leader,
|
120
|
+
"<"=>:high_priority,
|
121
|
+
"N"=>:low_priority,
|
122
|
+
"+"=>:foreground,
|
123
|
+
"L"=>:locked_pages,
|
124
|
+
"l"=>:multithreaded,
|
125
|
+
}
|
126
|
+
|
127
|
+
def initialize(*args)
|
128
|
+
@dead = false
|
129
|
+
args << stat_to_state(args[PS_FIELDS.index(:stat)])
|
130
|
+
super(*args)
|
131
|
+
end
|
132
|
+
|
133
|
+
def parent
|
134
|
+
Sys.ps(ppid).first unless ppid < 1
|
135
|
+
end
|
136
|
+
|
137
|
+
def children
|
138
|
+
@@parents ||= Sys.ps.group_by(&:ppid)
|
139
|
+
@@parents[pid]
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Convert all the process information to a hash.
|
144
|
+
#
|
145
|
+
def to_hash
|
146
|
+
Hash[ *members.zip(values).flatten(1) ]
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Send the TERM signal to this process.
|
151
|
+
#
|
152
|
+
def kill!(signal="TERM")
|
153
|
+
puts "Killing #{pid} (#{signal})"
|
154
|
+
Process.kill(signal, pid)
|
155
|
+
# TODO: handle exception Errno::ESRCH (no such process)
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Has this process been killed?
|
160
|
+
#
|
161
|
+
def dead?
|
162
|
+
@dead ||= Sys.pid(pid).empty?
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Refresh this process' statistics.
|
167
|
+
#
|
168
|
+
def refresh
|
169
|
+
processes = Sys.ps(pid)
|
170
|
+
|
171
|
+
if processes.empty?
|
172
|
+
@dead = true
|
173
|
+
raise ProcessNotFound
|
174
|
+
end
|
175
|
+
|
176
|
+
updated_process = processes.first
|
177
|
+
members.each { |member| self[member] = updated_process[member] }
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
alias_method :name, :command
|
182
|
+
|
183
|
+
# Linux-specific methods
|
184
|
+
if Sys.linux?
|
185
|
+
|
186
|
+
def exename
|
187
|
+
@exename ||= File.readlink("/proc/#{pid}/exe") rescue :unknown
|
188
|
+
@exename == :unknown ? nil : @exename
|
189
|
+
end
|
190
|
+
|
191
|
+
def fds
|
192
|
+
Dir["/proc/#{pid}/fd/*"].map { |fd| File.readlink(fd) rescue nil }
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def stat_to_state(str)
|
200
|
+
states = case Sys.os
|
201
|
+
when "Linux" then LINUX_STATES
|
202
|
+
when "Darwin" then DARWIN_STATES
|
203
|
+
else raise "Unsupported platform: #{Sys.os}"
|
204
|
+
end
|
205
|
+
|
206
|
+
str.scan(/./).map { |char| states[char] }.compact
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
data/lib/epitools/wm.rb
CHANGED
@@ -60,6 +60,7 @@ module WM
|
|
60
60
|
class Window < TypedStruct["window_id desktop_id:int pid:int x:int y:int w:int h:int hostname title"]
|
61
61
|
|
62
62
|
def self.all
|
63
|
+
# FIXME: Windows owned by linux-namespaced processes (firejail) report their namspaced pid to X11. `window.process` ends up pointing at either nil, or the wrong process.
|
63
64
|
`wmctrl -lpG`.lines.map(&:strip).map { |line| Window.from_line(line) }
|
64
65
|
end
|
65
66
|
|
data/spec/core_ext_spec.rb
CHANGED
@@ -674,9 +674,11 @@ describe Enumerator do
|
|
674
674
|
it "concatenates" do
|
675
675
|
e = [1,2,3].to_enum + [4,5,6].to_enum
|
676
676
|
e.to_a.should == [1,2,3,4,5,6]
|
677
|
+
e.size.should == 6
|
677
678
|
|
678
679
|
e = [1,2,3].to_enum + [4,5,6]
|
679
680
|
e.to_a.should == [1,2,3,4,5,6]
|
681
|
+
e.size.should == 6
|
680
682
|
end
|
681
683
|
|
682
684
|
it "multiplies" do
|
data/spec/path_spec.rb
CHANGED
@@ -21,6 +21,15 @@ describe Path do
|
|
21
21
|
path2.path.should_not == path.path
|
22
22
|
end
|
23
23
|
|
24
|
+
it "sizes" do
|
25
|
+
path = Path.tmpfile
|
26
|
+
path.write("asdfasdf")
|
27
|
+
path.size.should == 8
|
28
|
+
|
29
|
+
nonexistant = Path.new("/i/hope/this/doesn't/exist/man")
|
30
|
+
nonexistant.size.should == -1
|
31
|
+
end
|
32
|
+
|
24
33
|
it "works with relative paths" do
|
25
34
|
path = Path.new("../hello.mp3/blah")
|
26
35
|
|
data/spec/sys_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'epitools'
|
|
2
2
|
|
3
3
|
describe Sys::ProcessInfo do
|
4
4
|
|
5
|
-
specify "
|
5
|
+
specify "OS type" do
|
6
6
|
proc { Sys.os }.should_not raise_error
|
7
7
|
proc { Sys.linux? }.should_not raise_error
|
8
8
|
proc { Sys.mac? }.should_not raise_error
|
@@ -12,58 +12,41 @@ describe Sys::ProcessInfo do
|
|
12
12
|
|
13
13
|
%w[Linux Windows Darwin BSD].include?(Sys.os).should == true
|
14
14
|
|
15
|
-
[:linux?, :mac?, :windows?, :bsd?].any?{|os| Sys.send(os)}.should == true
|
15
|
+
[:linux?, :mac?, :windows?, :bsd?].any? { |os| Sys.send(os) }.should == true
|
16
16
|
end
|
17
17
|
|
18
18
|
|
19
19
|
specify "list all processes" do
|
20
|
-
#
|
21
|
-
|
22
|
-
# procs.first.state.is_a?(Array).should == true
|
23
|
-
#
|
24
|
-
# pids = procs.map{ |process| process.pid }
|
25
|
-
#
|
26
|
-
# p2s = Hash[ *Sys.ps(*pids).map { |process| [process.pid, process] }.flatten ]
|
27
|
-
# matches = 0
|
28
|
-
# procs.each do |p1|
|
29
|
-
# if p2 = p2s[p1.pid]
|
30
|
-
# matches += 1
|
31
|
-
# p1.command.should == p2.command
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# matches.should > 1
|
36
|
-
end
|
20
|
+
# Sys.ps.should_not be_blank
|
21
|
+
procs = Sys.ps
|
37
22
|
|
23
|
+
procs.first.state.is_a?(Array).should == true
|
38
24
|
|
39
|
-
|
25
|
+
pids = procs.map{ |process| process.pid }
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# print "."
|
50
|
-
# rescue Sys::ProcessNotFound
|
51
|
-
# end
|
52
|
-
# end.should_not raise_error
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# puts
|
27
|
+
p2s = Hash[ *Sys.ps(*pids).map { |process| [process.pid, process] }.flatten ]
|
28
|
+
matches = 0
|
29
|
+
procs.each do |p1|
|
30
|
+
if p2 = p2s[p1.pid]
|
31
|
+
matches += 1
|
32
|
+
p1.command.should == p2.command
|
33
|
+
end
|
34
|
+
end
|
56
35
|
|
36
|
+
matches.should > 1
|
57
37
|
end
|
58
38
|
|
59
|
-
|
60
|
-
specify "cross-platform method" do
|
39
|
+
specify "cross-platform methods" do
|
61
40
|
Sys.cross_platform_method(:cross_platform_test)
|
62
|
-
proc{ Sys.cross_platform_test }.should raise_error
|
41
|
+
proc { Sys.cross_platform_test }.should raise_error(NotImplementedError)
|
63
42
|
end
|
64
43
|
|
65
|
-
specify "interfaces" do
|
44
|
+
specify "network interfaces" do
|
66
45
|
Sys.interfaces.should_not be_blank
|
67
46
|
end
|
68
47
|
|
48
|
+
specify "mounts" do
|
49
|
+
Sys.mounts.should_not be_blank
|
50
|
+
end
|
51
|
+
|
69
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epitools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.107
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -80,6 +80,12 @@ files:
|
|
80
80
|
- lib/epitools/rash.rb
|
81
81
|
- lib/epitools/ratio.rb
|
82
82
|
- lib/epitools/sys.rb
|
83
|
+
- lib/epitools/sys/mem.rb
|
84
|
+
- lib/epitools/sys/misc.rb
|
85
|
+
- lib/epitools/sys/mounts.rb
|
86
|
+
- lib/epitools/sys/net.rb
|
87
|
+
- lib/epitools/sys/os.rb
|
88
|
+
- lib/epitools/sys/ps.rb
|
83
89
|
- lib/epitools/term.rb
|
84
90
|
- lib/epitools/trie.rb
|
85
91
|
- lib/epitools/typed_struct.rb
|
@@ -124,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
130
|
version: '0'
|
125
131
|
requirements: []
|
126
132
|
rubyforge_project:
|
127
|
-
rubygems_version: 2.6.
|
133
|
+
rubygems_version: 2.6.11
|
128
134
|
signing_key:
|
129
135
|
specification_version: 3
|
130
136
|
summary: Not utils... METILS!
|