linux_stat 1.0.1 → 1.2.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/README.md +250 -92
- data/ext/sysconf/sysconf.c +20 -9
- data/lib/linux_stat.rb +2 -1
- data/lib/linux_stat/net.rb +6 -2
- data/lib/linux_stat/os.rb +57 -20
- data/lib/linux_stat/pci.rb +363 -0
- data/lib/linux_stat/usb.rb +67 -11
- data/lib/linux_stat/version.rb +1 -1
- metadata +4 -3
data/ext/sysconf/sysconf.c
CHANGED
@@ -19,28 +19,28 @@ static VALUE getTick(VALUE obj) {
|
|
19
19
|
}
|
20
20
|
|
21
21
|
static VALUE getChildMax(VALUE obj) {
|
22
|
-
|
22
|
+
long long int val = sysconf(_SC_CHILD_MAX) ;
|
23
23
|
if (val < 0) return Qnil ;
|
24
24
|
|
25
25
|
return INT2FIX(val) ;
|
26
26
|
}
|
27
27
|
|
28
28
|
static VALUE getHostnameMax(VALUE obj) {
|
29
|
-
|
29
|
+
int val = sysconf(_SC_HOST_NAME_MAX) ;
|
30
30
|
if (val < 0) return Qnil ;
|
31
31
|
|
32
32
|
return INT2FIX(val) ;
|
33
33
|
}
|
34
34
|
|
35
35
|
static VALUE getLoginNameMax(VALUE obj) {
|
36
|
-
|
36
|
+
int val = sysconf(_SC_LOGIN_NAME_MAX) ;
|
37
37
|
if (val < 0) return Qnil ;
|
38
38
|
|
39
39
|
return INT2FIX(val) ;
|
40
40
|
}
|
41
41
|
|
42
42
|
static VALUE getOpenMax(VALUE obj) {
|
43
|
-
|
43
|
+
int val = sysconf(_SC_OPEN_MAX) ;
|
44
44
|
if (val < 0) return Qnil ;
|
45
45
|
|
46
46
|
return INT2FIX(val) ;
|
@@ -54,35 +54,35 @@ static VALUE getPageSize(VALUE obj) {
|
|
54
54
|
}
|
55
55
|
|
56
56
|
static VALUE getStreamMax(VALUE obj) {
|
57
|
-
|
57
|
+
int val = sysconf(_SC_STREAM_MAX) ;
|
58
58
|
if (val < 0) return Qnil ;
|
59
59
|
|
60
60
|
return INT2FIX(val) ;
|
61
61
|
}
|
62
62
|
|
63
63
|
static VALUE getTTYNameMax(VALUE obj) {
|
64
|
-
|
64
|
+
int val = sysconf(_SC_TTY_NAME_MAX) ;
|
65
65
|
if (val < 0) return Qnil ;
|
66
66
|
|
67
67
|
return INT2FIX(val) ;
|
68
68
|
}
|
69
69
|
|
70
70
|
static VALUE getPosixVersion(VALUE obj) {
|
71
|
-
|
71
|
+
int val = sysconf(_SC_VERSION) ;
|
72
72
|
if (val < 0) return Qnil ;
|
73
73
|
|
74
74
|
return INT2FIX(val) ;
|
75
75
|
}
|
76
76
|
|
77
77
|
static VALUE getLineMax(VALUE obj) {
|
78
|
-
|
78
|
+
int val = sysconf(_SC_LINE_MAX) ;
|
79
79
|
if (val < 0) return Qnil ;
|
80
80
|
|
81
81
|
return INT2FIX(val) ;
|
82
82
|
}
|
83
83
|
|
84
84
|
static VALUE getExprNestMax(VALUE obj) {
|
85
|
-
|
85
|
+
int val = sysconf(_SC_EXPR_NEST_MAX) ;
|
86
86
|
if (val < 0) return Qnil ;
|
87
87
|
|
88
88
|
return INT2FIX(val) ;
|
@@ -119,6 +119,15 @@ static VALUE getEUID(VALUE obj) {
|
|
119
119
|
return INT2FIX(geteuid()) ;
|
120
120
|
}
|
121
121
|
|
122
|
+
static VALUE getHostname(VALUE obj) {
|
123
|
+
int h_max = sysconf(_SC_HOST_NAME_MAX) + 1 ;
|
124
|
+
char hostname[h_max] ;
|
125
|
+
|
126
|
+
short int status = gethostname(hostname, h_max) ;
|
127
|
+
|
128
|
+
return (status < 0) ? rb_str_new_cstr("") : rb_str_new_cstr(hostname) ;
|
129
|
+
}
|
130
|
+
|
122
131
|
void Init_sysconf() {
|
123
132
|
VALUE _linux_stat = rb_define_module("LinuxStat") ;
|
124
133
|
VALUE _sysconf = rb_define_module_under(_linux_stat, "Sysconf") ;
|
@@ -144,4 +153,6 @@ void Init_sysconf() {
|
|
144
153
|
|
145
154
|
rb_define_module_function(_sysconf, "get_user", getUser, 0) ;
|
146
155
|
rb_define_module_function(_sysconf, "get_login", getUser, 0) ;
|
156
|
+
|
157
|
+
rb_define_module_function(_sysconf, "hostname", getHostname, 0) ;
|
147
158
|
}
|
data/lib/linux_stat.rb
CHANGED
@@ -15,8 +15,8 @@
|
|
15
15
|
|
16
16
|
# Miscellaneous Modules
|
17
17
|
# Independed and LinuxStat's miscellaneous modules
|
18
|
-
require "linux_stat/version"
|
19
18
|
require 'linux_stat/prettify_bytes'
|
19
|
+
require "linux_stat/version"
|
20
20
|
|
21
21
|
# Independed Modules
|
22
22
|
# Modules that doesn't have any dependency on its own
|
@@ -25,6 +25,7 @@ require "linux_stat/battery"
|
|
25
25
|
require "linux_stat/bios"
|
26
26
|
require "linux_stat/memory"
|
27
27
|
require "linux_stat/net"
|
28
|
+
require "linux_stat/pci"
|
28
29
|
require "linux_stat/process"
|
29
30
|
require "linux_stat/swap"
|
30
31
|
require "linux_stat/usb"
|
data/lib/linux_stat/net.rb
CHANGED
@@ -17,7 +17,9 @@ module LinuxStat
|
|
17
17
|
# Returns the total bytes received and transmitted as Hash.
|
18
18
|
#
|
19
19
|
# For example:
|
20
|
-
#
|
20
|
+
# LinuxStat::Net.usage
|
21
|
+
#
|
22
|
+
# => {:received=>56602867, :transmitted=>6940922}
|
21
23
|
#
|
22
24
|
# But if the status isn't available it will return an empty Hash.
|
23
25
|
def total_bytes
|
@@ -68,7 +70,9 @@ module LinuxStat
|
|
68
70
|
# The return type is a Hash, containg the current internet usage (received, transmit) in B/s.
|
69
71
|
#
|
70
72
|
# For example:
|
71
|
-
#
|
73
|
+
# LinuxStat::Net.usage
|
74
|
+
#
|
75
|
+
# => {:received=>436060.0, :transmitted=>50350.0}
|
72
76
|
#
|
73
77
|
# If the system transmits 100 kb in the interval,
|
74
78
|
#
|
data/lib/linux_stat/os.rb
CHANGED
@@ -3,7 +3,9 @@ module LinuxStat
|
|
3
3
|
class << self
|
4
4
|
##
|
5
5
|
# Reads /etc/os-release and returns a Hash. For example:
|
6
|
-
#
|
6
|
+
# LinuxStat::OS.os_release
|
7
|
+
#
|
8
|
+
# => {:NAME=>"Arch Linux", :PRETTY_NAME=>"Arch Linux", :ID=>"arch", :BUILD_ID=>"rolling", :ANSI_COLOR=>"38;2;23;147;209", :HOME_URL=>"https://www.archlinux.org/", :DOCUMENTATION_URL=>"https://wiki.archlinux.org/", :SUPPORT_URL=>"https://bbs.archlinux.org/", :BUG_REPORT_URL=>"https://bugs.archlinux.org/", :LOGO=>"archlinux"}
|
7
9
|
#
|
8
10
|
# If the info isn't available, it will return an empty Hash.
|
9
11
|
#
|
@@ -13,13 +15,14 @@ module LinuxStat
|
|
13
15
|
# Because changing the /etc/lsb-release
|
14
16
|
# isn't expected in runtime.
|
15
17
|
def os_release
|
16
|
-
# cached (memoized) ; as changing the value in runtime is unexpected
|
17
18
|
@@os_release ||= File.readable?('/etc/os-release') ? release('/etc/os-release') : {}
|
18
19
|
end
|
19
20
|
|
20
21
|
##
|
21
22
|
# Reads /etc/lsb-release and returns a Hash. For example:
|
22
|
-
#
|
23
|
+
# LinuxStat::OS.lsb_release
|
24
|
+
#
|
25
|
+
# => {:LSB_VERSION=>"1.4", :DISTRIB_ID=>"Arch", :DISTRIB_RELEASE=>"rolling", :DISTRIB_DESCRIPTION=>"Arch Linux"}
|
23
26
|
#
|
24
27
|
# If the info isn't available, it will return an empty Hash.
|
25
28
|
#
|
@@ -28,7 +31,6 @@ module LinuxStat
|
|
28
31
|
# The information is also cached, and once loaded, won't change in runtime.
|
29
32
|
# Because changing the /etc/lsb-release isn't expected in runtime.
|
30
33
|
def lsb_release
|
31
|
-
# cached (memoized) ; as changing the value in runtime is unexpected
|
32
34
|
@@lsb_release ||= File.readable?('/etc/lsb-release') ? release('/etc/lsb-release') : {}
|
33
35
|
end
|
34
36
|
|
@@ -49,14 +51,46 @@ module LinuxStat
|
|
49
51
|
v[:DISTRIB_DESCRIPTION]
|
50
52
|
elsif v.key?(:DISTRIB_ID)
|
51
53
|
v[:DISTRIB_ID]
|
52
|
-
elsif File.readable?('/etc/issue')
|
53
|
-
IO.read('/etc/issue').strip
|
54
|
+
elsif File.readable?('/etc/issue'.freeze)
|
55
|
+
IO.read('/etc/issue'.freeze, encoding: 'ASCII-8bit').strip
|
54
56
|
else
|
55
57
|
'Unknown'.freeze
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
62
|
+
##
|
63
|
+
# Gives you the version of the OS you are using.
|
64
|
+
#
|
65
|
+
# On ArchLinux for example:
|
66
|
+
# LinuxStat::OS.version
|
67
|
+
#
|
68
|
+
# => "rolling"
|
69
|
+
#
|
70
|
+
# On Ubuntu 20.04:
|
71
|
+
# LinuxStat::OS.version
|
72
|
+
#
|
73
|
+
# => "20.04"
|
74
|
+
#
|
75
|
+
# On CentOS 26:
|
76
|
+
# LinuxStat::OS.version
|
77
|
+
#
|
78
|
+
# => "26"
|
79
|
+
#
|
80
|
+
# The return type is String.
|
81
|
+
# But if the information isn't available, it will return an empty frozen String.
|
82
|
+
def version
|
83
|
+
@@distrib_version ||= if os_release.key?(:VERSION_ID)
|
84
|
+
os_release[:VERSION_ID]
|
85
|
+
elsif lsb_release.key?(:DISTRIB_RELEASE)
|
86
|
+
lsb_release[:DISTRIB_RELEASE]
|
87
|
+
elsif os_release.key?(:VERSION)
|
88
|
+
os_release[:VERSION]
|
89
|
+
else
|
90
|
+
''.freeze
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
60
94
|
##
|
61
95
|
# Uses utsname.h to determine the machine
|
62
96
|
#
|
@@ -68,22 +102,18 @@ module LinuxStat
|
|
68
102
|
##
|
69
103
|
# Uses utsname.h to determine the system nodename
|
70
104
|
#
|
71
|
-
# It returns String but if the info isn't available, it will return an empty String
|
105
|
+
# It returns String but if the info isn't available, it will return an empty String.
|
72
106
|
def nodename
|
73
|
-
|
107
|
+
LinuxStat::Uname.nodename
|
74
108
|
end
|
75
109
|
|
76
110
|
##
|
77
|
-
#
|
111
|
+
# Returns the hostname from LinuxStat::Sysconf.hostname.
|
78
112
|
#
|
79
113
|
# The return type is String.
|
80
|
-
# If the info info isn't available, it will return
|
114
|
+
# If the info info isn't available, it will return an empty String.
|
81
115
|
def hostname
|
82
|
-
|
83
|
-
IO.read('/etc/hostname').strip
|
84
|
-
else
|
85
|
-
'localhost'
|
86
|
-
end
|
116
|
+
LinuxStat::Sysconf.hostname
|
87
117
|
end
|
88
118
|
|
89
119
|
##
|
@@ -104,13 +134,15 @@ module LinuxStat
|
|
104
134
|
|
105
135
|
##
|
106
136
|
# Reads /proc/uptime and returns the system uptime:
|
107
|
-
#
|
137
|
+
# LinuxStat::OS.uptime
|
138
|
+
#
|
139
|
+
# => {:hour=>10, :minute=>34, :second=>12.59}
|
108
140
|
#
|
109
141
|
# If the stat isn't available, an empty hash is returned.
|
110
142
|
def uptime
|
111
143
|
return {} unless uptime_readable?
|
112
144
|
|
113
|
-
uptime = IO.read('/proc/uptime').to_f
|
145
|
+
uptime = IO.read('/proc/uptime'.freeze).to_f
|
114
146
|
uptime_i = uptime.to_i
|
115
147
|
|
116
148
|
h = uptime_i / 3600
|
@@ -124,16 +156,21 @@ module LinuxStat
|
|
124
156
|
}.freeze
|
125
157
|
end
|
126
158
|
|
159
|
+
alias distrib_version version
|
160
|
+
|
127
161
|
private
|
128
162
|
def release(file)
|
129
163
|
IO.readlines(file, 4096).reduce({}) { |h, x|
|
130
164
|
x.strip!
|
131
165
|
next h if x.empty?
|
132
166
|
|
133
|
-
splitted = x.split(
|
167
|
+
splitted = x.split(?=.freeze)
|
168
|
+
|
169
|
+
key = splitted[0].to_s
|
170
|
+
key.strip!
|
134
171
|
|
135
|
-
|
136
|
-
value
|
172
|
+
value = splitted[1..-1].join(?=.freeze).to_s
|
173
|
+
value.strip!
|
137
174
|
|
138
175
|
value[0] = ''.freeze if value[0] == ?".freeze
|
139
176
|
value[-1] = ''.freeze if value[-1] == ?".freeze
|
@@ -0,0 +1,363 @@
|
|
1
|
+
module LinuxStat
|
2
|
+
module PCI
|
3
|
+
class << self
|
4
|
+
##
|
5
|
+
# = devices_info(hwdata: true)
|
6
|
+
#
|
7
|
+
# [ Not to be confused with devices_stat ]
|
8
|
+
#
|
9
|
+
# Take a look at LinuxStat::PCI.devices_stat for more info.
|
10
|
+
#
|
11
|
+
# Returns details about the devices found in /proc/bus/pci/devices file.
|
12
|
+
#
|
13
|
+
# The details doesn't contain a lot of details, it opens just one file.
|
14
|
+
#
|
15
|
+
# The return value is an Array of multiple Hashes. If there's no info available,
|
16
|
+
# it will rather return an empty Array.
|
17
|
+
#
|
18
|
+
# On Android Termux for example, it can not list the directories because they are
|
19
|
+
# not readable the the regular user.
|
20
|
+
#
|
21
|
+
# It can have information like:
|
22
|
+
#
|
23
|
+
# id, vendor, device, irq, and kernel_driver
|
24
|
+
#
|
25
|
+
# An example of the returned sample from a test machine is:
|
26
|
+
# LinuxStat::PCI.devices_info
|
27
|
+
#
|
28
|
+
# => [{:id=>"8086:1904", :vendor=>"8086", :device=>"1904", :irq=>0, :kernel_driver=>"skl_uncore", :hwdata=>{:vendor=>"Intel Corporation", :product=>"Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers"}}]
|
29
|
+
#
|
30
|
+
# Right, it's an Array of Hashes.
|
31
|
+
#
|
32
|
+
# It also takes one option. The hwdata, which is true by default.
|
33
|
+
#
|
34
|
+
# Information about usb devices is found inside /usr/share/hwdata/pci.ids
|
35
|
+
#
|
36
|
+
# The data contains the vendor and the product, the subvendor and the subproduct.
|
37
|
+
#
|
38
|
+
# If the option is enabled, it will try read /usr/share/hwdata/pci.ids
|
39
|
+
#
|
40
|
+
# But the file will be read only once. The consecutive calls to this method
|
41
|
+
# won't open the hwdata all the times.
|
42
|
+
#
|
43
|
+
# But if there's no hwdata, the Hash returned by this method will not contain
|
44
|
+
# hwdata key.
|
45
|
+
#
|
46
|
+
# The data is only populated if it's available. For example, if there's no
|
47
|
+
# manufacturer available for the product, the returned Hash will not contain the
|
48
|
+
# information about the manufacturer.
|
49
|
+
#
|
50
|
+
# Also note that if there's no info available or no PCI enabled devices, it will return an empty
|
51
|
+
# Hash.
|
52
|
+
def devices_info(hwdata: true)
|
53
|
+
@@proc_pci_readable ||= File.readable?('/proc/bus/pci/devices')
|
54
|
+
return {} unless @@proc_pci_readable
|
55
|
+
|
56
|
+
IO.readlines('/proc/bus/pci/devices'.freeze).map! { |dev|
|
57
|
+
x = dev.split
|
58
|
+
|
59
|
+
vendor = x[1][0..3]
|
60
|
+
device = x[1][4..-1]
|
61
|
+
irq = x[2].to_i(16)
|
62
|
+
kernel_driver = x[-1]
|
63
|
+
|
64
|
+
query = if hwdata
|
65
|
+
query_hwdata(vendor, device)
|
66
|
+
else
|
67
|
+
{}
|
68
|
+
end
|
69
|
+
|
70
|
+
ret = {
|
71
|
+
id: "#{vendor}:#{device}",
|
72
|
+
vendor: vendor, device: device,
|
73
|
+
irq: irq,
|
74
|
+
kernel_driver: kernel_driver
|
75
|
+
}
|
76
|
+
|
77
|
+
ret.merge!(hwdata: query) unless query.empty?
|
78
|
+
|
79
|
+
ret
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# = devices_stat(hwdata: true)
|
85
|
+
#
|
86
|
+
# Returns details about the devices found in /sys/bus/pci/devices/
|
87
|
+
#
|
88
|
+
# The return value is an Array of multiple Hashes. If there's no info available,
|
89
|
+
# it will rather return an empty Array.
|
90
|
+
#
|
91
|
+
# On Android Termux for example, it can not list the directories because they are
|
92
|
+
# not readable the the regular user.
|
93
|
+
#
|
94
|
+
# It can have information like:
|
95
|
+
#
|
96
|
+
# path, id, vendor, device, subvendor, sub_device,
|
97
|
+
# kernel_driver, revision, irq, enable, hwdata.
|
98
|
+
#
|
99
|
+
# An example of the returned sample from a test machine is:
|
100
|
+
# LinuxStat::PCI.devices_stat
|
101
|
+
#
|
102
|
+
# => [{:path=>"/sys/bus/pci/devices/0000:00:00.0/", :id=>"8086:1904", :vendor=>"8086", :device=>"1904", :sub_vendor=>"1028", :sub_device=>"077d", :kernel_driver=>"skl_uncore", :revision=>"0x08", :irq=>0, :enable=>false, :hwdata=>{:vendor=>"Intel Corporation", :product=>"Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers", :sub_system=>nil}}]
|
103
|
+
#
|
104
|
+
# Right, it's an Array of Hashes.
|
105
|
+
#
|
106
|
+
# It also takes one option. The hwdata, which is true by default.
|
107
|
+
#
|
108
|
+
# Information about usb devices is found inside /usr/share/hwdata/pci.ids
|
109
|
+
#
|
110
|
+
# The data contains the vendor and the product, the subvendor and the subproduct.
|
111
|
+
#
|
112
|
+
# If the option is enabled, it will try read /usr/share/hwdata/pci.ids
|
113
|
+
#
|
114
|
+
# But the file will be read only once. The consecutive calls to this method
|
115
|
+
# won't open the hwdata all the times.
|
116
|
+
#
|
117
|
+
# But if there's no hwdata, the Hash returned by this method will not contain
|
118
|
+
# hwdata key.
|
119
|
+
#
|
120
|
+
# The data is only populated if it's available. For example, if there's no
|
121
|
+
# manufacturer available for the product, the returned Hash will not contain the
|
122
|
+
# information about the manufacturer.
|
123
|
+
#
|
124
|
+
# If there's no /sys/bus/pci/devices/, it will call LinuxStat::PCI.devices_info
|
125
|
+
#
|
126
|
+
# Also note that if there's no info available or no PCI enabled devices, it will return an empty
|
127
|
+
# Hash.
|
128
|
+
def devices_stat(hwdata: true)
|
129
|
+
@@sys_pci_readable ||= File.readable?('/sys/bus/pci/devices/')
|
130
|
+
return devices_info(hwdata: hwdata) unless @@sys_pci_readable
|
131
|
+
|
132
|
+
Dir['/sys/bus/pci/devices/*/'.freeze].sort!.map! { |x|
|
133
|
+
_vendor_file = File.join(x, 'vendor'.freeze)
|
134
|
+
next unless File.readable?(_vendor_file)
|
135
|
+
vendor = IO.read(_vendor_file).to_i(16).to_s(16)
|
136
|
+
prepend_0(vendor)
|
137
|
+
|
138
|
+
_device_file = File.join(x, 'device'.freeze)
|
139
|
+
next unless File.readable?(_device_file)
|
140
|
+
device = IO.read(_device_file).to_i(16).to_s(16)
|
141
|
+
prepend_0(device)
|
142
|
+
|
143
|
+
_sub_vendor_file = File.join(x, 'subsystem_vendor'.freeze)
|
144
|
+
sub_vendor = File.readable?(_sub_vendor_file) ? IO.read(_sub_vendor_file).to_i(16).to_s(16) : nil
|
145
|
+
prepend_0(sub_vendor) if sub_vendor
|
146
|
+
|
147
|
+
_sub_device_file = File.join(x, 'subsystem_device'.freeze)
|
148
|
+
sub_device = File.readable?(_sub_device_file) ? IO.read(_sub_device_file).to_i(16).to_s(16) : nil
|
149
|
+
prepend_0(sub_device) if sub_device
|
150
|
+
|
151
|
+
_uevent = File.join(x, 'uevent'.freeze)
|
152
|
+
uevent = File.readable?(_uevent) ? IO.foreach(_uevent) : nil
|
153
|
+
|
154
|
+
kernel_driver = if uevent
|
155
|
+
uevent.find { |_x|
|
156
|
+
_x.split(?=.freeze)[0].to_s.tap(&:strip!) == 'DRIVER'.freeze
|
157
|
+
} &.split(?=) &.[](1) &.tap(&:strip!)
|
158
|
+
else
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
162
|
+
_revision_file = File.join(x, 'revision'.freeze)
|
163
|
+
revision = File.readable?(_revision_file) ? IO.read(_revision_file).tap(&:strip!) : ''.freeze
|
164
|
+
|
165
|
+
_irq_file = File.join(x, 'irq'.freeze)
|
166
|
+
irq = File.readable?(_irq_file) ? IO.read(_irq_file).to_i : nil
|
167
|
+
|
168
|
+
_enable_file = File.join(x, 'enable'.freeze)
|
169
|
+
enable = File.readable?(_enable_file) ? IO.read(_enable_file).to_i == 1 : nil
|
170
|
+
|
171
|
+
query = if hwdata && sub_vendor && sub_device
|
172
|
+
query_hwdata(vendor, device, sub_vendor, sub_device)
|
173
|
+
elsif hwdata && sub_vendor
|
174
|
+
query_hwdata(vendor, device, sub_vendor)
|
175
|
+
elsif hwdata
|
176
|
+
query_hwdata(vendor, device)
|
177
|
+
else
|
178
|
+
{}
|
179
|
+
end
|
180
|
+
|
181
|
+
ret = {
|
182
|
+
path: x, id: "#{vendor}:#{device}",
|
183
|
+
vendor: vendor, device: device
|
184
|
+
}
|
185
|
+
|
186
|
+
ret.merge!(sub_vendor: sub_vendor) if sub_vendor
|
187
|
+
ret.merge!(sub_device: sub_device) if sub_device
|
188
|
+
|
189
|
+
ret.merge!(kernel_driver: kernel_driver) if kernel_driver
|
190
|
+
ret.merge!(revision: revision) unless revision.empty?
|
191
|
+
ret.merge!(irq: irq) if irq
|
192
|
+
ret.merge!(enable: enable) unless enable.nil?
|
193
|
+
ret.merge!(hwdata: query) unless query.empty?
|
194
|
+
|
195
|
+
ret
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Reads /proc/bus/pci/devices, counts and returns the total number of lines.
|
201
|
+
#
|
202
|
+
# But if the information isn't available, it will look into the contents of
|
203
|
+
# /sys/bus/pci/devices, and counts the total number of
|
204
|
+
# devices connected to the PCI.
|
205
|
+
# It checks for devices with vendor and device id files.
|
206
|
+
# If there's no such file, it will not count them as a PCI connected device.
|
207
|
+
#
|
208
|
+
# The return type is an integer.
|
209
|
+
#
|
210
|
+
# But if the information isn't available, it will return nil.
|
211
|
+
def count
|
212
|
+
@@proc_pci_readable ||= File.readable?('/proc/bus/pci/devices')
|
213
|
+
@@sys_pci_readable ||= File.readable?('/sys/bus/pci/devices/')
|
214
|
+
|
215
|
+
if @@proc_pci_readable
|
216
|
+
IO.readlines('/proc/bus/pci/devices'.freeze).length
|
217
|
+
|
218
|
+
elsif @@sys_pci_readable
|
219
|
+
Dir['/sys/bus/pci/devices/*/'.freeze].count { |x|
|
220
|
+
id_vendor_file = File.join(x, 'vendor'.freeze)
|
221
|
+
id_product_file = File.join(x, 'device'.freeze)
|
222
|
+
File.readable?(id_vendor_file) && File.readable?(id_product_file)
|
223
|
+
}
|
224
|
+
|
225
|
+
else
|
226
|
+
nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# hwdata_file = file
|
232
|
+
#
|
233
|
+
# Lets you set the hwdata_file about pci.ids.
|
234
|
+
#
|
235
|
+
# The hwdata file about pci.ids contains vendor name and product name information about
|
236
|
+
# devices. This is then mapped by the other methods that utilizes hwdata/pci.ids.
|
237
|
+
#
|
238
|
+
# Do note that this method is intended to run only once, at the beginning.
|
239
|
+
# If you use any other method that utilizes hwdata/pci.ids, before
|
240
|
+
# calling this method, this method will not work.
|
241
|
+
def hwdata_file=(file)
|
242
|
+
@@hwdata_file ||= file.freeze
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Checks if hwdata_file is already initialized or not.
|
247
|
+
# Once it's initialized, calling hwdata_file = 'something/pci.ids' is futile.
|
248
|
+
def hwdata_file_set?
|
249
|
+
@@hwdata_file ||= false
|
250
|
+
!!@@hwdata_file
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Returns the hwdata_file as string.
|
255
|
+
#
|
256
|
+
# If hwdata_file isn't set, it will return an empty frozen string.
|
257
|
+
#
|
258
|
+
# Once it's set, it can't be changed.
|
259
|
+
def hwdata_file
|
260
|
+
@@hwdata_file ||= nil
|
261
|
+
@@hwdata_file ? @@hwdata_file : ''.freeze
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# Initializes hwdata
|
266
|
+
#
|
267
|
+
# hwdata can take upto 0.1 to 0.2 seconds to get initialized.
|
268
|
+
#
|
269
|
+
# Calling this method will load hwdata for future use.
|
270
|
+
#
|
271
|
+
# Once it's initialized, hwdata_file can't be changed.
|
272
|
+
#
|
273
|
+
# If this method initializes hwdata, it will return true
|
274
|
+
# Othewise this method will return false.
|
275
|
+
def initialize_hwdata
|
276
|
+
@@hwdata ||= nil
|
277
|
+
init = !@@hwdata
|
278
|
+
hwdata
|
279
|
+
init
|
280
|
+
end
|
281
|
+
|
282
|
+
alias count_devices count
|
283
|
+
|
284
|
+
private
|
285
|
+
def hwdata
|
286
|
+
@@hwdata_file ||= "/usr/share/hwdata/pci.ids".freeze
|
287
|
+
|
288
|
+
@@hwdata ||= if File.readable?(@@hwdata_file)
|
289
|
+
ret, vendor_id, device_id, device = {}, nil, nil, nil
|
290
|
+
i = -1
|
291
|
+
|
292
|
+
file_data = IO.readlines(@@hwdata_file, encoding: 'ASCII-8BIT')
|
293
|
+
file_data_size = file_data.size
|
294
|
+
|
295
|
+
while (i += 1) < file_data_size
|
296
|
+
x = file_data[i]
|
297
|
+
|
298
|
+
_lstripped = x.lstrip
|
299
|
+
next if _lstripped[0] == ?#.freeze || _lstripped.empty?
|
300
|
+
|
301
|
+
if x[0..1] == "\t\t".freeze
|
302
|
+
next unless device_id
|
303
|
+
|
304
|
+
x.strip!
|
305
|
+
sub_device_id = x[/\A.*?\s/].to_s.strip
|
306
|
+
sub_device = x[device_id.length..-1].to_s.strip
|
307
|
+
|
308
|
+
if ret[vendor_id] &.at(1) &.[](device_id) &.[](1)
|
309
|
+
sub_system_id = sub_device[/\A.*?\s/].to_s.strip
|
310
|
+
sub_system_device = sub_device[sub_system_id.length..-1].to_s.strip
|
311
|
+
|
312
|
+
ret[vendor_id][1][device_id][1].merge!(sub_device_id => {sub_system_id => sub_system_device})
|
313
|
+
end
|
314
|
+
|
315
|
+
elsif x[0] == ?\t.freeze
|
316
|
+
next unless vendor_id
|
317
|
+
|
318
|
+
x.strip!
|
319
|
+
device_id = x[/\A.*?\s/].to_s.strip
|
320
|
+
device = x[device_id.length..-1].to_s.strip
|
321
|
+
ret[vendor_id][1][device_id] = [device, {}]
|
322
|
+
|
323
|
+
else
|
324
|
+
x.strip!
|
325
|
+
vendor_id = x[/\A.*?\s/].to_s.strip
|
326
|
+
vendor = x[vendor_id.length..-1].to_s.strip
|
327
|
+
ret[vendor_id] = [vendor, {}]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
ret.freeze
|
332
|
+
else
|
333
|
+
{}
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def prepend_0(n)
|
338
|
+
n[0, 0] = ?0.*(4 - n.length).freeze if n.length < 4
|
339
|
+
n
|
340
|
+
end
|
341
|
+
|
342
|
+
def query_hwdata(vendor_id, product_id, sub_vendor_id = nil, sub_device_id = nil)
|
343
|
+
vendor = hwdata[vendor_id]
|
344
|
+
|
345
|
+
if vendor
|
346
|
+
ret = {
|
347
|
+
vendor: vendor[0],
|
348
|
+
product: vendor.dig(1, product_id, 0),
|
349
|
+
}
|
350
|
+
|
351
|
+
if sub_vendor_id && sub_device_id
|
352
|
+
sub_product = vendor.dig(1, product_id, 1, sub_vendor_id, sub_device_id)
|
353
|
+
ret.merge!(sub_system: sub_product) if sub_product
|
354
|
+
end
|
355
|
+
|
356
|
+
ret
|
357
|
+
else
|
358
|
+
{}
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|