linux_stat 2.4.0 → 2.5.0
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/exe/linuxstat.rb +5 -5
- data/ext/misc/integer/extconf.rb +2 -0
- data/ext/misc/integer/integer?.c +63 -0
- data/lib/linux_stat/battery.rb +1 -1
- data/lib/linux_stat/cpu.rb +82 -6
- data/lib/linux_stat/mounts.rb +3 -3
- data/lib/linux_stat/net.rb +2 -2
- data/lib/linux_stat/os.rb +2 -1
- data/lib/linux_stat/pci.rb +9 -9
- data/lib/linux_stat/process.rb +3 -3
- data/lib/linux_stat/process_info.rb +1 -1
- data/lib/linux_stat/swap.rb +2 -1
- data/lib/linux_stat/thermal.rb +4 -4
- data/lib/linux_stat/usb.rb +10 -10
- data/lib/linux_stat/user.rb +10 -4
- data/lib/linux_stat/version.rb +1 -1
- data/lib/linux_stat.rb +1 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 938eb126f7b43a731ec3d8de134f606411ff2245011eca7416782daaddd4220d
|
4
|
+
data.tar.gz: 8c171887831dcbd04b24f6301ba9a8450bc9510708c3c931aac6c256444b2477
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 362dfab83cc06f4b84eee112fd0e5ca405b75ad91c94639eed76c3961514ea56d75e6f263624140a08f64298b2a080753d247f2ee3ce7d3be3a9c0023e3d4d53
|
7
|
+
data.tar.gz: 14c6912aba0cf41e85fb6e79ba0c71496512b5b740db8457fe8af4f68588e06601170823a00ecab5fcef88e6c28feb8fcb98ee27c3c42bcff2ee11ac61a5194d
|
data/exe/linuxstat.rb
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
T_FMT = '%d'.freeze
|
12
12
|
|
13
13
|
# Check the number of iterations
|
14
|
-
iterations = (ARGV.find { |x|
|
14
|
+
iterations = (ARGV.find { |x| LinuxStat::Misc.integer?(x) } || 1).to_i
|
15
15
|
|
16
16
|
Integer.class_exec do
|
17
17
|
define_method(:clamp) { |min, max|
|
@@ -73,14 +73,14 @@ conflicting.each do |x, y, z|
|
|
73
73
|
rev = ARGV.reverse
|
74
74
|
|
75
75
|
if rev.index { |_x| _x[y] } < rev.index { |_x| _x[z] }
|
76
|
-
hash.
|
76
|
+
hash.store(o1, true)
|
77
77
|
else
|
78
|
-
hash.
|
78
|
+
hash.store(o2, true)
|
79
79
|
end
|
80
80
|
elsif m1
|
81
|
-
hash.
|
81
|
+
hash.store(o1, true)
|
82
82
|
elsif m2
|
83
|
-
hash.
|
83
|
+
hash.store(o2, true)
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
/*
|
2
|
+
Validates if a String is integer or not.
|
3
|
+
|
4
|
+
Conditions:
|
5
|
+
- The String can start with - (negative)
|
6
|
+
- If the Argument is a BigInt or Integer, or anything else, return false
|
7
|
+
- The String cannot have anything other than 0 to 9
|
8
|
+
- The String can have leading zeroes and negative sign:
|
9
|
+
Example 1: "-00999" which translates to Ruby's -999 (decimal)
|
10
|
+
Example 2: "000999" translates to 999
|
11
|
+
- If it fails to determine, it returns nil instead of false
|
12
|
+
- It doesn't raise any error. Handing nil is enough to indicate that it failed.
|
13
|
+
*/
|
14
|
+
|
15
|
+
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
16
|
+
#pragma GCC optimize ("O3")
|
17
|
+
#pragma GCC diagnostic warning "-Wall"
|
18
|
+
#elif defined(__clang__)
|
19
|
+
#pragma clang optimize on
|
20
|
+
#pragma clang diagnostic warning "-Wall"
|
21
|
+
#elif defined(__INTEL_COMPILER)
|
22
|
+
#pragma intel optimization_level 3
|
23
|
+
#endif
|
24
|
+
|
25
|
+
#include <limits.h>
|
26
|
+
#include "ruby.h"
|
27
|
+
|
28
|
+
VALUE isNumber(volatile VALUE obj, volatile VALUE val) {
|
29
|
+
// But we don't expect anything other than String though as Argument.
|
30
|
+
// Note that raising ArgumentError or any kind of Error shouldn't be done here
|
31
|
+
// Otherwise Integer(n) is the best method in Ruby.
|
32
|
+
if (!RB_TYPE_P(val, T_STRING))
|
33
|
+
return Qnil ;
|
34
|
+
|
35
|
+
char *str = StringValuePtr(val) ;
|
36
|
+
char ch = str[0] ;
|
37
|
+
|
38
|
+
// If the string is empty, return false
|
39
|
+
if (!ch) return Qfalse ;
|
40
|
+
|
41
|
+
unsigned char i = ch == '-' ? 1 : 0 ;
|
42
|
+
if (!str[i]) return Qfalse ;
|
43
|
+
|
44
|
+
unsigned char max = UCHAR_MAX ;
|
45
|
+
|
46
|
+
# pragma GCC unroll 4
|
47
|
+
while((ch = str[i++])) {
|
48
|
+
if (ch < 48 || ch > 57)
|
49
|
+
return Qfalse ;
|
50
|
+
|
51
|
+
if (i == max)
|
52
|
+
return Qnil ;
|
53
|
+
}
|
54
|
+
|
55
|
+
return Qtrue ;
|
56
|
+
}
|
57
|
+
|
58
|
+
void Init_integer() {
|
59
|
+
VALUE linuxStat = rb_define_module("LinuxStat") ;
|
60
|
+
VALUE misc = rb_define_module_under(linuxStat, "Misc") ;
|
61
|
+
|
62
|
+
rb_define_module_function(misc, "integer?", isNumber, 1) ;
|
63
|
+
}
|
data/lib/linux_stat/battery.rb
CHANGED
data/lib/linux_stat/cpu.rb
CHANGED
@@ -46,7 +46,8 @@ module LinuxStat
|
|
46
46
|
res = totald.-(idle_now - idle_then).fdiv(totald).abs.*(100)
|
47
47
|
res = res.nan? ? 0.0 : res > 100 ? 100.0 : res.round(2)
|
48
48
|
|
49
|
-
h.
|
49
|
+
h.store(x, res)
|
50
|
+
h
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -203,7 +204,7 @@ module LinuxStat
|
|
203
204
|
|
204
205
|
h = {}
|
205
206
|
@@cur_f.each { |id, file|
|
206
|
-
h.
|
207
|
+
h.store(id, IO.read(file).to_i) if File.readable?(file)
|
207
208
|
}
|
208
209
|
|
209
210
|
h
|
@@ -229,7 +230,7 @@ module LinuxStat
|
|
229
230
|
|
230
231
|
h = {}
|
231
232
|
@@min_f.each { |id, file|
|
232
|
-
h.
|
233
|
+
h.store(id, IO.read(file).to_i) if File.readable?(file)
|
233
234
|
}
|
234
235
|
|
235
236
|
h
|
@@ -251,7 +252,7 @@ module LinuxStat
|
|
251
252
|
|
252
253
|
h = {}
|
253
254
|
@@min_f.each { |id, file|
|
254
|
-
h.
|
255
|
+
h.store(id, IO.read(file).to_i) if File.readable?(file)
|
255
256
|
}
|
256
257
|
|
257
258
|
h
|
@@ -275,7 +276,7 @@ module LinuxStat
|
|
275
276
|
|
276
277
|
h = {}
|
277
278
|
@@scaling_g.each { |id, file|
|
278
|
-
h.
|
279
|
+
h.store(id, IO.read(file).tap(&:strip!)) if File.readable?(file)
|
279
280
|
}
|
280
281
|
|
281
282
|
h
|
@@ -297,14 +298,89 @@ module LinuxStat
|
|
297
298
|
|
298
299
|
h = {}
|
299
300
|
@@scaling_av_g.each { |id, file|
|
300
|
-
h.
|
301
|
+
h.store(id, IO.read(file).split.each(&:strip!)) if File.readable?(file)
|
301
302
|
}
|
302
303
|
|
303
304
|
h
|
304
305
|
end
|
305
306
|
|
307
|
+
##
|
308
|
+
# Returns the number of physical cores on the system.
|
309
|
+
#
|
310
|
+
# The return value is an Array of Integers. Each number denoting the physical processor number.
|
311
|
+
# You can later use this to schedule tasks or something else (not provided by LinuxStat).
|
312
|
+
#
|
313
|
+
# However, if the information isn't available on /sys/devices/system/cpu[0-9]*/topology/thread_siblings_list, it will return an empty Array.
|
314
|
+
def physical_core_list
|
315
|
+
physical_cores = []
|
316
|
+
hyperthreaded = {}
|
317
|
+
|
318
|
+
entries = Dir.entries('/sys/devices/system/cpu/'.freeze)
|
319
|
+
entries.delete(?..freeze)
|
320
|
+
entries.delete('..'.freeze)
|
321
|
+
|
322
|
+
entries.each do |x|
|
323
|
+
if x[0..2] == 'cpu'.freeze && LinuxStat::Misc.integer?(x[3..-1])
|
324
|
+
file = "/sys/devices/system/cpu/#{x}/topology/thread_siblings_list"
|
325
|
+
next unless File.readable?(file)
|
326
|
+
|
327
|
+
data = IO.read(file)
|
328
|
+
data.strip!
|
329
|
+
|
330
|
+
val = data.split(?,.freeze).map(&:to_i)
|
331
|
+
val.shift
|
332
|
+
|
333
|
+
# Add items has for fast lookup.
|
334
|
+
# This hash includes all hyper threaded cores that doesn't map to anything.
|
335
|
+
# But this hash has the purpose to look up for items and not include in the list of physical_cores
|
336
|
+
# This is just an array, but can look for keys in O(1), so it's faster than ary.find() { ... }.
|
337
|
+
val.each { |x| hyperthreaded.store(x, nil) }
|
338
|
+
|
339
|
+
key = x[3..-1].to_i
|
340
|
+
physical_cores << key unless hyperthreaded.key?(key)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
physical_cores
|
345
|
+
end
|
346
|
+
|
347
|
+
##
|
348
|
+
# Returns the number of physical cores on the system.
|
349
|
+
#
|
350
|
+
# The return value is an Array of Integers. Each number denoting the hyperthreaded processor number.
|
351
|
+
# You can later use this to schedule tasks or something else (not provided by LinuxStat).
|
352
|
+
#
|
353
|
+
# However, if the information isn't available on /sys/devices/system/cpu[0-9]*/topology/thread_siblings_list, it will return an empty Array.
|
354
|
+
def hyperthreaded_core_list
|
355
|
+
hyperthreaded = {}
|
356
|
+
|
357
|
+
entries = Dir.entries('/sys/devices/system/cpu/'.freeze)
|
358
|
+
entries.delete(?..freeze)
|
359
|
+
entries.delete('..'.freeze)
|
360
|
+
|
361
|
+
entries.each do |x|
|
362
|
+
if x[0..2] == 'cpu'.freeze && LinuxStat::Misc.integer?(x[3..-1])
|
363
|
+
file = "/sys/devices/system/cpu/#{x}/topology/thread_siblings_list"
|
364
|
+
next unless File.readable?(file)
|
365
|
+
|
366
|
+
data = IO.read(file)
|
367
|
+
data.strip!
|
368
|
+
|
369
|
+
val = data.split(?,.freeze).map(&:to_i)
|
370
|
+
val.shift
|
371
|
+
|
372
|
+
# Add items has for fast lookup to get rid of duplicate items.
|
373
|
+
val.each { |x| hyperthreaded.store(x, nil) unless hyperthreaded.key?(x) }
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
hyperthreaded.keys
|
378
|
+
end
|
379
|
+
|
306
380
|
alias usages stat
|
307
381
|
alias usage total_usage
|
382
|
+
alias physical_cores physical_core_list
|
383
|
+
alias hyperthreaded_cores hyperthreaded_core_list
|
308
384
|
|
309
385
|
private
|
310
386
|
def cpuinfo
|
data/lib/linux_stat/mounts.rb
CHANGED
@@ -58,7 +58,7 @@ module LinuxStat
|
|
58
58
|
def tmpfs
|
59
59
|
ret = {}
|
60
60
|
mounts.each { |x|
|
61
|
-
ret.
|
61
|
+
ret.store(x.split[1], x) if x.start_with?('tmpfs '.freeze)
|
62
62
|
}
|
63
63
|
ret
|
64
64
|
end
|
@@ -115,7 +115,7 @@ module LinuxStat
|
|
115
115
|
|
116
116
|
unless x.empty?
|
117
117
|
_x = x.split
|
118
|
-
m.
|
118
|
+
m.store(_x[0], _x[1])
|
119
119
|
end
|
120
120
|
end
|
121
121
|
m
|
@@ -146,7 +146,7 @@ module LinuxStat
|
|
146
146
|
_x = x.split
|
147
147
|
total, free, available, used = fs_info(_x[1])
|
148
148
|
|
149
|
-
m.
|
149
|
+
m.store(_x[0], {
|
150
150
|
mountpoint: _x[1],
|
151
151
|
|
152
152
|
total: total,
|
data/lib/linux_stat/net.rb
CHANGED
@@ -120,8 +120,8 @@ module LinuxStat
|
|
120
120
|
|
121
121
|
r.each_with_index { |x, i|
|
122
122
|
downcased = x.downcase
|
123
|
-
h.
|
124
|
-
h.
|
123
|
+
h.store(:r, i) if downcased.start_with?('receive'.freeze)
|
124
|
+
h.store(:t, i) if downcased.start_with?('transmit'.freeze)
|
125
125
|
}
|
126
126
|
|
127
127
|
data_0 = data.next.gsub(?|.freeze, ' %'.freeze)
|
data/lib/linux_stat/os.rb
CHANGED
data/lib/linux_stat/pci.rb
CHANGED
@@ -76,7 +76,7 @@ module LinuxStat
|
|
76
76
|
kernel_driver: kernel_driver
|
77
77
|
}
|
78
78
|
|
79
|
-
ret.
|
79
|
+
ret.store(:hwdata, query) unless query.empty?
|
80
80
|
|
81
81
|
ret
|
82
82
|
}
|
@@ -186,14 +186,14 @@ module LinuxStat
|
|
186
186
|
vendor: vendor, device: device
|
187
187
|
}
|
188
188
|
|
189
|
-
ret.
|
190
|
-
ret.
|
189
|
+
ret.store(:sub_vendor, sub_vendor) if sub_vendor
|
190
|
+
ret.store(:sub_device, sub_device) if sub_device
|
191
191
|
|
192
|
-
ret.
|
193
|
-
ret.
|
194
|
-
ret.
|
195
|
-
ret.
|
196
|
-
ret.
|
192
|
+
ret.store(:kernel_driver, kernel_driver) if kernel_driver
|
193
|
+
ret.store(:revision, revision) unless revision.empty?
|
194
|
+
ret.store(:irq, irq) if irq
|
195
|
+
ret.store(:enable, enable) unless enable.nil?
|
196
|
+
ret.store(:hwdata, query) unless query.empty?
|
197
197
|
|
198
198
|
ret
|
199
199
|
rescue StandardError
|
@@ -355,7 +355,7 @@ module LinuxStat
|
|
355
355
|
|
356
356
|
if sub_vendor_id && sub_device_id
|
357
357
|
sub_product = vendor.dig(1, product_id, 1, sub_vendor_id, sub_device_id)
|
358
|
-
ret.
|
358
|
+
ret.store(:sub_system, sub_product) if sub_product
|
359
359
|
end
|
360
360
|
|
361
361
|
ret
|
data/lib/linux_stat/process.rb
CHANGED
@@ -33,7 +33,7 @@ module LinuxStat
|
|
33
33
|
x = l[i]
|
34
34
|
|
35
35
|
begin
|
36
|
-
h.
|
36
|
+
h.store(x, IO.read("/proc/#{x}/comm").strip)
|
37
37
|
rescue StandardError
|
38
38
|
end
|
39
39
|
end
|
@@ -56,7 +56,7 @@ module LinuxStat
|
|
56
56
|
begin
|
57
57
|
cmdlines = IO.read("/proc/#{x}/cmdline").strip
|
58
58
|
cmdlines.gsub!(?\u0000.freeze, ?\s.freeze)
|
59
|
-
h.
|
59
|
+
h.store(x, cmdlines)
|
60
60
|
rescue StandardError
|
61
61
|
end
|
62
62
|
end
|
@@ -75,7 +75,7 @@ module LinuxStat
|
|
75
75
|
x = l[i]
|
76
76
|
|
77
77
|
begin
|
78
|
-
h.
|
78
|
+
h.store(x,
|
79
79
|
case LinuxStat::ProcFS.ps_state(x)
|
80
80
|
when ?S.freeze then :sleeping
|
81
81
|
when ?I.freeze then :idle
|
data/lib/linux_stat/swap.rb
CHANGED
@@ -13,7 +13,8 @@ module LinuxStat
|
|
13
13
|
file = IO.readlines('/proc/swaps'.freeze).drop(1)
|
14
14
|
file.reduce({}) do |h, x|
|
15
15
|
name, *stats = x.strip.split
|
16
|
-
h.
|
16
|
+
h.store(name, stats.map! { |v| LinuxStat::Misc.integer?(v) ? v.to_i : v.to_sym })
|
17
|
+
h
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
data/lib/linux_stat/thermal.rb
CHANGED
@@ -107,10 +107,10 @@ module LinuxStat
|
|
107
107
|
|
108
108
|
h = {path: path, name: name}
|
109
109
|
|
110
|
-
h.
|
111
|
-
h.
|
112
|
-
h.
|
113
|
-
h.
|
110
|
+
h.store(:label, label) if label
|
111
|
+
h.store(key, value)
|
112
|
+
h.store(:temp_crit, temp_crit) if temp_crit
|
113
|
+
h.store(:temp_crit, temp_max) if temp_max
|
114
114
|
|
115
115
|
ret.push(h)
|
116
116
|
end
|
data/lib/linux_stat/usb.rb
CHANGED
@@ -102,20 +102,20 @@ module LinuxStat
|
|
102
102
|
vendor_id: id_vendor, product_id: id_product
|
103
103
|
}
|
104
104
|
|
105
|
-
ret.
|
106
|
-
ret.
|
105
|
+
ret.store(:bus_num, bus_num.to_i) unless bus_num.empty?
|
106
|
+
ret.store(:dev_num, dev_num.to_i) unless dev_num.empty?
|
107
107
|
|
108
|
-
ret.
|
108
|
+
ret.store(:serial, serial) unless serial.empty?
|
109
109
|
|
110
|
-
ret.
|
111
|
-
ret.
|
112
|
-
ret.
|
110
|
+
ret.store(:hwdata, query) unless query.empty?
|
111
|
+
ret.store(:product, product) unless product.empty?
|
112
|
+
ret.store(:manufacturer, manufacturer) unless manufacturer.empty?
|
113
113
|
|
114
|
-
ret.
|
115
|
-
ret.
|
114
|
+
ret.store(:removable, is_removable) unless is_removable.nil?
|
115
|
+
ret.store(:authorized, authorized == 1)
|
116
116
|
|
117
|
-
ret.
|
118
|
-
ret.
|
117
|
+
ret.store(:b_max_power, b_max_power) unless b_max_power.empty?
|
118
|
+
ret.store(:b_max_packet_size0, b_max_packet_size0) if b_max_packet_size0
|
119
119
|
|
120
120
|
ret
|
121
121
|
rescue StandardError
|
data/lib/linux_stat/user.rb
CHANGED
@@ -25,9 +25,11 @@ module LinuxStat
|
|
25
25
|
def ids
|
26
26
|
return {} unless passwd_readable?
|
27
27
|
passwd_splitted.reduce({}) { |h, x|
|
28
|
-
h.
|
28
|
+
h.store(x[0].to_sym, {
|
29
29
|
uid: x[2].to_i, gid: x[3].to_i
|
30
30
|
})
|
31
|
+
|
32
|
+
h
|
31
33
|
}
|
32
34
|
end
|
33
35
|
|
@@ -42,8 +44,10 @@ module LinuxStat
|
|
42
44
|
def uids
|
43
45
|
return {} unless passwd_readable?
|
44
46
|
passwd_splitted.reduce({}) { |h, x|
|
45
|
-
h.
|
47
|
+
h.store(x[0].to_sym, x[2].to_i)
|
48
|
+
h
|
46
49
|
}
|
50
|
+
|
47
51
|
end
|
48
52
|
|
49
53
|
##
|
@@ -59,7 +63,8 @@ module LinuxStat
|
|
59
63
|
return {} unless passwd_readable?
|
60
64
|
|
61
65
|
passwd_splitted.reduce({}) { |h, x|
|
62
|
-
h.
|
66
|
+
h.store(x[0].to_sym, x[3].to_i)
|
67
|
+
h
|
63
68
|
}
|
64
69
|
end
|
65
70
|
|
@@ -76,7 +81,8 @@ module LinuxStat
|
|
76
81
|
return {} unless passwd_readable?
|
77
82
|
passwd.reduce({}) { |h, x|
|
78
83
|
splitted = x.split(?:)
|
79
|
-
h.
|
84
|
+
h.store(splitted[0].to_sym, splitted[5])
|
85
|
+
h
|
80
86
|
}
|
81
87
|
end
|
82
88
|
|
data/lib/linux_stat/version.rb
CHANGED
data/lib/linux_stat.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linux_stat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sourav Goswami
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Linux only, efficient linux system utilization reporting and system monitoring
|
14
14
|
gem
|
@@ -18,6 +18,7 @@ executables:
|
|
18
18
|
- linuxstat.rb
|
19
19
|
extensions:
|
20
20
|
- ext/fs_stat/extconf.rb
|
21
|
+
- ext/misc/integer/extconf.rb
|
21
22
|
- ext/nproc/extconf.rb
|
22
23
|
- ext/procfs/extconf.rb
|
23
24
|
- ext/sysconf/extconf.rb
|
@@ -35,6 +36,8 @@ files:
|
|
35
36
|
- ext/fs_stat/extconf.rb
|
36
37
|
- ext/fs_stat/fs_stat.c
|
37
38
|
- ext/fs_stat/sector_size.h
|
39
|
+
- ext/misc/integer/extconf.rb
|
40
|
+
- ext/misc/integer/integer?.c
|
38
41
|
- ext/nproc/extconf.rb
|
39
42
|
- ext/nproc/nproc.c
|
40
43
|
- ext/procfs/extconf.rb
|
@@ -87,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
90
|
- !ruby/object:Gem::Version
|
88
91
|
version: '0'
|
89
92
|
requirements: []
|
90
|
-
rubygems_version: 3.2.
|
93
|
+
rubygems_version: 3.2.29
|
91
94
|
signing_key:
|
92
95
|
specification_version: 4
|
93
96
|
summary: Efficient linux system reporting gem
|