linux_stat 0.1.1 → 0.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.
@@ -1,6 +1,9 @@
1
1
  module LinuxStat
2
2
  module BIOS
3
3
  class << self
4
+ # Returns the model of the BIOS.
5
+ # If the information is not available it will return a frozen empty string.
6
+ # The output is also cached ; as changing the value in runtime is unexpected.
4
7
  def model
5
8
  # Cached ; as changing the value in runtime is unexpected
6
9
  @@model ||= if File.readable?('/sys/devices/virtual/dmi/id/product_name')
@@ -12,6 +15,9 @@ module LinuxStat
12
15
  end
13
16
  end
14
17
 
18
+ # Returns the vendor of the BIOS.
19
+ # If the information is not available it will return a frozen empty string.
20
+ # The output is also cached ; as changing the value in runtime is unexpected.
15
21
  def vendor
16
22
  # Cached ; as changing the value in runtime is unexpected
17
23
  @@vendor ||= if File.readable?('/sys/devices/virtual/dmi/id/bios_vendor')
@@ -21,8 +27,10 @@ module LinuxStat
21
27
  end
22
28
  end
23
29
 
30
+ # Returns the version of the BIOS.
31
+ # If the information is not available it will return a frozen empty string.
32
+ # The output is also cached ; as changing the value in runtime is unexpected.
24
33
  def version
25
- # Cached ; as changing the value in runtime is unexpected
26
34
  @@version ||= if File.readable?('/sys/devices/virtual/dmi/id/bios_version')
27
35
  IO.read('/sys/devices/virtual/dmi/id/bios_version').tap(&:strip!)
28
36
  else
@@ -30,8 +38,10 @@ module LinuxStat
30
38
  end
31
39
  end
32
40
 
41
+ # Returns the date of the BIOS.
42
+ # If the information is not available it will return a frozen empty string.
43
+ # The output is also cached ; as changing the value in runtime is unexpected.
33
44
  def date
34
- # Cached ; as changing the value in runtime is unexpected
35
45
  @@date ||= if File.readable?('/sys/devices/virtual/dmi/id/bios_date')
36
46
  IO.read('/sys/devices/virtual/dmi/id/bios_date').tap(&:strip!)
37
47
  else
@@ -1,6 +1,15 @@
1
1
  module LinuxStat
2
2
  module CPU
3
3
  class << self
4
+ # Returns the cpu usage of all threads.
5
+ #
6
+ # The first one is aggregated CPU usage reported by the Linux kernel.
7
+ # And the consecutive ones are the real core usages.
8
+ #
9
+ # On a system with 4 threads, the output will be like::
10
+ # {0=>84.38, 1=>100.0, 2=>50.0, 3=>87.5, 4=>87.5}
11
+ #
12
+ # If the information is not available, it will return an empty Hash
4
13
  def stat(sleep = 0.075)
5
14
  return {} unless stat?
6
15
 
@@ -21,35 +30,56 @@ module LinuxStat
21
30
  end
22
31
  end
23
32
 
33
+ # Returns the total cpu usage.
34
+ # It's like running LinuxStat::CPU.stat[0] but it's much more efficient and calculates just the aggregated usage which is available at the top of the /proc/stat file.
35
+ #
36
+ # If the information is not available, it will return nil.
37
+ def total_usage(sleep = 0.075)
38
+ return nil unless stat?
39
+
40
+ data = IO.foreach('/proc/stat').first.split.tap(&:shift).map!(&:to_f)
41
+ sleep(sleep)
42
+ data2 = IO.foreach('/proc/stat').first.split.tap(&:shift).map!(&:to_f)
43
+
44
+ user, nice, sys, idle, iowait, irq, softirq, steal = *data
45
+ user2, nice2, sys2, idle2, iowait2, irq2, softirq2, steal2 = *data2
46
+
47
+ idle_then, idle_now = idle + iowait, idle2 + iowait2
48
+ totald = idle_now.+(user2 + nice2 + sys2 + irq2 + softirq2 + steal2) - idle_then.+(user + nice + sys + irq + softirq + steal)
49
+ totald.-(idle_now - idle_then).fdiv(totald).*(100).round(2).abs
50
+ end
51
+
52
+ # Returns the total number of CPU threads.
53
+ # If the information isn't available, it will return 0.
24
54
  def count
25
55
  # CPU count can change during the program runtime
26
56
  cpuinfo.count { |x| x.start_with?('processor') }
27
57
  end
28
58
 
59
+ # Returns the model of processor.
60
+ # If the information isn't available, it will return en empty string.
61
+ # The output is also cached ; as changing the value in runtime is unexpected.
29
62
  def model
30
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
31
- # to add/remove CPUs during program runtime
32
63
  @@name ||= cpuinfo.find { |x| x.start_with?('model name') }.to_s.split(?:)[-1].to_s.strip
33
64
  end
34
65
 
35
- # Returns an array with current core frequencies corresponding to the usages
66
+ # Returns an array with current core frequencies corresponding to the usages.
67
+ # If the information isn't available, it will return an empty array.
36
68
  def cur_freq
37
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
38
- # to add/remove CPUs during program runtime
39
-
40
69
  @@cpu_freqs ||= Dir["/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq"]
41
70
  @@cpu_freqs.map { |x| IO.read(x).to_i }
42
71
  end
43
72
 
44
- # Returns an array with max core frequencies corresponding to the usages
73
+ # Returns an array with max core frequencies corresponding to the usages.
74
+ # If the information isn't available, it will return an empty array.
45
75
  def max_freq
46
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
47
- # to add/remove CPUs during program runtime
48
-
49
76
  @@max_freqs ||= Dir["/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_max_freq"]
50
77
  @@max_freqs.map { |x| IO.read(x).to_i }
51
78
  end
52
79
 
80
+ alias usages stat
81
+ alias usage total_usage
82
+
53
83
  private
54
84
  def cpuinfo
55
85
  File.readable?('/proc/cpuinfo') ? IO.readlines('/proc/cpuinfo') : []
@@ -1,37 +1,147 @@
1
1
  module LinuxStat
2
2
  module Kernel
3
3
  class << self
4
+ # Returns the Linux Kernel version.
5
+ # If the information isn't available, it will return a frozen empty string.
6
+ # The output is also cached ; as changing the value in runtime is unexpected.
4
7
  def version
5
- return @@version ||= ''.freeze if string.empty?
6
- @@version ||= string.split[2]
8
+ return ''.freeze if string.empty?
9
+ @@version ||= splitted[2]
7
10
  end
8
11
 
12
+ # Returns the name of the user who built the kernel using KBUILD_FLAGS.
13
+ # If the information isn't available, it will return a frozen empty string.
14
+ # The output is also cached ; as changing the value in runtime is unexpected.
15
+ def build_user
16
+ @@build_user ||= string.split(/(\(.+\))/).each(&:strip!)
17
+ .reject(&:empty?).find { |x| x[/^\(.+\)$/] }.to_s
18
+ .split[0].to_s[1..-2].to_s.freeze
19
+ end
20
+
21
+ # Returns the compiler used to compile the Linux Kernel.
22
+ # If the information isn't available, it will return a frozen empty string.
23
+ # The output is also cached ; as changing the value in runtime is unexpected.
9
24
  def compiler
10
- return @@compiler ||= ''.freeze if string.empty?
25
+ return ''.freeze if string.empty?
26
+
27
+ @@compiler ||= string.split(/(\(.+\))/).each(&:strip!)
28
+ .reject(&:empty?)
29
+ .find { |x| x[/^\(.+\)$/] }.to_s
30
+ .split.find { |x| !x[/^(.+@.+)$/] }.to_s[/\w+/].to_s.freeze
11
31
 
12
- @@compiler ||= case string.split[4].to_s
32
+ @@compiler_val ||= case @@compiler
13
33
  when /gcc/i then [:gcc ]
14
34
  when /clang/i then [:clang]
15
35
  when /icc/i then [:icc]
16
- end << string[/\(.*\)/].split.drop(1).find { |x| x[/^\d.*\d/] }[/^\d.*\d/]
36
+ else [@@compiler &.to_sym]
37
+ end << compiler_version
38
+ end
39
+
40
+ # Returns the compiler version used to compile the Linux Kernel.
41
+ # If the information isn't available, it will return a frozen empty string.
42
+ # The output is also cached ; as changing the value in runtime is unexpected.
43
+ def compiler_version
44
+ @@compiler_version ||= string.split(/(\(.+?\))/).each(&:strip!)
45
+ .reject(&:empty?)[2..4].to_a
46
+ .find { |x| x[/[\d.]+/] }.to_s[/[\d.]+/].to_s.freeze
17
47
  end
18
48
 
49
+ # Returns the time when the kernel was compiled.
50
+ # The return value is a Time object.
51
+ # If the information isn't available, it will return nil
52
+ #
53
+ # The time will be searched in specific order.
54
+ # It will match any date matching any of these formats:
55
+ # 1. %b %d %H:%M:%S %z %Y
56
+ # 2. %d %b %Y %H:%M:%S %z
57
+ # 3. %Y-%m-%d
58
+ # Most kernels have date in them in this format.
59
+ #
60
+ # Do note that Ruby sometimes fails to work with timezones like BST for example.
61
+ # In such case, the timezone is unrealiable and often returns the local timezone.
62
+ # You have to use regexp yourself to get the proper zone.
63
+ # Use LinuxStat::Kernel.build_date_string to get the original string if you need that.
64
+ #
65
+ # The output is also cached ; as changing the value in runtime is unexpected.
19
66
  def build_date
20
- return @@time ||= Time.new(0) if string.empty?
67
+ return nil if splitted.empty?
21
68
 
22
69
  @@time ||= begin
23
- require 'time'
24
- Time.strptime(string.split[16..-1].join(' '), "%d %b %Y %H:%M:%S %z")
25
- rescue StandardError
26
- Time.new(0)
70
+ require 'time' unless Time.respond_to?(:strptime)
71
+
72
+ splitted.each_cons(5).map do |x|
73
+ joined = x.each(&:strip!).join(?\s.freeze)
74
+
75
+ # Match 21 Oct 2020 01:11:20 +0000
76
+ if joined[/^\d{2}\s\w{3}\s\d{4}\s\d{2}:\d{2}:\d{2}\s\+\d*$/]
77
+ Time.strptime(joined, '%d %b %Y %H:%M:%S %Z') rescue nil
78
+
79
+ # Match Aug 25 17:23:54 UTC 2020
80
+ elsif joined[/^\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w+\s\d*$/]
81
+ Time.strptime(joined, '%b %d %H:%M:%S %z %Y') rescue nil
82
+
83
+ # Match 2017-09-19
84
+ elsif joined[/\d{4}-\d{2}-\d{2}/]
85
+ Time.strptime(joined[/\d{4}-\d{2}-\d{2}/] + " +00:00", '%Y-%m-%d %z') rescue nil
86
+
87
+ else
88
+ nil
89
+ end
90
+ end.tap(&:compact!)[0]
27
91
  end
28
92
  end
29
93
 
94
+ # Returns the time when the kernel was compiled.
95
+ # The return value is a String.
96
+ # If the information isn't available, it will return nil
97
+ #
98
+ # The time will be searched in specific order.
99
+ # It will match any date matching any of these formats:
100
+ # 1. %b %d %H:%M:%S %z %Y
101
+ # 2. %d %b %Y %H:%M:%S %z
102
+ # 3. %Y-%m-%d
103
+ # Most kernels have date in them in this format.
104
+ #
105
+ # Do note that Ruby sometimes fails to work with timezones like BST for example.
106
+ # In such case, the timezone is unrealiable and often returns the local timezone.
107
+ # You have to use regexp yourself to get the proper zone.
108
+ # Use LinuxStat::Kernel.build_date_string to get the original string if you need that.
109
+ #
110
+ # The output is also cached ; as changing the value in runtime is unexpected.
111
+ def build_date_string
112
+ return nil if splitted.empty?
113
+
114
+ @@time2 ||= begin
115
+ require 'time' unless Time.respond_to?(:strptime)
116
+
117
+ splitted.each_cons(5).map do |x|
118
+ joined = x.each(&:strip!).join(?\s.freeze)
119
+
120
+ # Match 21 Oct 2020 01:11:20 +0000
121
+ if (joined[/^\d{2}\s\w{3}\s\d{4}\s\d{2}:\d{2}:\d{2}\s\+\d*$/] && (Time.strptime(joined, '%d %b %Y %H:%M:%S %Z') rescue nil)) ||
122
+
123
+ # Match Aug 25 17:23:54 UTC 2020
124
+ (joined[/^\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w+\s\d*$/] && (Time.strptime(joined, '%b %d %H:%M:%S %z %Y') rescue nil)) ||
125
+
126
+ # Match 2017-09-19
127
+ (joined[/\d{4}-\d{2}-\d{2}/] && (Time.strptime(joined[/\d{4}-\d{2}-\d{2}/] + " +00:00", '%Y-%m-%d %z') rescue nil))
128
+ joined
129
+ else
130
+ nil
131
+ end
132
+ end.tap(&:compact!)[0]
133
+ end
134
+ end
135
+
136
+ # Reads maximum 1024 bytes from /proc/version and returns the string.
137
+ # The output is also cached ; as changing the value in runtime is unexpected.
30
138
  def string
31
- # Cached ; as changing the value in runtime is unexpected
32
- # Hotfix update can be problem, but it's rare and might not
33
- # affect the version string during program runtime.
34
- @@string ||= File.readable?('/proc/version') ? IO.read('/proc/version').tap(&:strip!) : ''
139
+ @@string ||= File.readable?('/proc/version') ? IO.read('/proc/version', 1024).tap(&:strip!) : ''
140
+ end
141
+
142
+ private
143
+ def splitted
144
+ @@string_splitted ||= string.split
35
145
  end
36
146
  end
37
147
  end
@@ -1,6 +1,11 @@
1
1
  module LinuxStat
2
2
  module Memory
3
3
  class << self
4
+ # Returns the memory details reported by /proc/meminfo. In this format:
5
+ # {:total=>3836264, :used=>3097952, :available=>738312, :percent_used=>80.75, :percent_available=>19.25}
6
+ #
7
+ # The value is in Kilobyte.
8
+ # If the statistics is not available, it will return an empty Hash.
4
9
  def stat
5
10
  return {} unless meminfo?
6
11
 
@@ -24,31 +29,44 @@ module LinuxStat
24
29
  }
25
30
  end
26
31
 
32
+ # Returns the total memory details reported by /proc/meminfo.
33
+ # The value is in Kilobyte.
34
+ # It retuns an Integer but if the info is not available, it will return nil.
27
35
  def total
28
- return 0 unless meminfo?
36
+ return nil unless meminfo?
29
37
  IO.foreach('/proc/meminfo').first.split[1].to_i
30
38
  end
31
39
 
40
+ # Returns the total memory details reported by /proc/meminfo.
41
+ # The value is in Kilobyte.
42
+ # It retuns an Integer but if the info is not available, it will return nil
32
43
  def available
33
- return 0 unless meminfo?
44
+ return nil unless meminfo?
34
45
  IO.foreach('/proc/meminfo').first(3)[-1].split[1].to_i
35
46
  end
36
47
 
48
+ # Returns the amount of memory used reported by /proc/meminfo.
49
+ # The value is in Kilobyte.
50
+ # It retuns an Integer but if the info is not available, it will return nil.
37
51
  def used
38
- return 0 unless meminfo?
52
+ return nil unless meminfo?
39
53
  memory = IO.foreach('/proc/meminfo').first(3)
40
54
  memory[0].split[1].to_i - memory[2].split[1].to_i
41
55
  end
42
56
 
57
+ # Returns the percentage of memory used reported by /proc/meminfo.
58
+ # It retuns an Integer but if the info is not available, it will return nil
43
59
  def percent_used
44
- return 0.0 unless meminfo?
60
+ return nil unless meminfo?
45
61
  memory = IO.foreach('/proc/meminfo').first(3)
46
62
  total = memory[0].split[1].to_i
47
63
  total.-(memory[2].split[1].to_i).*(100).fdiv(total).round(2)
48
64
  end
49
65
 
66
+ # Returns the percentage of memory used reported by /proc/meminfo.
67
+ # It retuns an Integer but if the info is not available, it will return nil
50
68
  def percent_available
51
- return 0.0 unless meminfo?
69
+ return nil unless meminfo?
52
70
  memory = IO.foreach('/proc/meminfo').first(3)
53
71
  memory[2].split[1].to_i.*(100).fdiv(memory[0].split[1].to_i).round(2)
54
72
  end
@@ -1,10 +1,12 @@
1
1
  module LinuxStat
2
2
  module Net
3
3
  class << self
4
+ # Returns the local IP address of the system as a String.
5
+ # If the information isn't available, it will a frozen empty string.
4
6
  def ipv4_private
5
7
  require 'socket'
6
8
  ip = Socket.ip_address_list.find(&:ipv4_private?)
7
- ip ? ip.ip? ? ip.ip_unpack[0].freeze : '' : ''
9
+ ip ? ip.ip? ? ip.ip_unpack[0].freeze : ''.freeze : ''.freeze
8
10
  end
9
11
  end
10
12
  end
@@ -1,18 +1,83 @@
1
1
  module LinuxStat
2
2
  module OS
3
3
  class << self
4
+ # Reads /etc/os-release and returns a Hash. For example:
5
+ # {: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"}
6
+ #
7
+ # If the info isn't available, it will return an empty Hash.
8
+ #
9
+ # The amount of data read is 4096 bytes. Any more than that will result in truncated output.
10
+ #
11
+ # The information is also cached, and once loaded, won't change in runtime. Because changing the /etc/lsb-release
12
+ # isn't expected in runtime.
4
13
  def os_release
5
14
  # Cached ; as changing the value in runtime is unexpected
6
15
  @@os_release ||= File.readable?('/etc/os-release') ? release('/etc/os-release') : {}
7
16
  end
8
17
 
18
+ # Reads /etc/lsb-release and returns a Hash. For example:
19
+ # {:LSB_VERSION=>"1.4", :DISTRIB_ID=>"Arch", :DISTRIB_RELEASE=>"rolling", :DISTRIB_DESCRIPTION=>"Arch Linux"}
20
+ #
21
+ # If the info isn't available, it will return an empty Hash.
22
+ #
23
+ # The amount of data read is 4096 bytes. Any more than that will result in truncated output.
24
+ #
25
+ # The information is also cached, and once loaded, won't change in runtime. Because changing the /etc/lsb-release
26
+ # isn't expected in runtime.
9
27
  def lsb_release
10
28
  # Cached ; as changing the value in runtime is unexpected
11
29
  @@lsb_release ||= File.readable?('/etc/lsb-release') ? release('/etc/lsb-release') : {}
12
30
  end
13
31
 
32
+ # Reads /etc/lsb-release or /etc/os-release tries to get information about the distribution.
33
+ # If the information isn't available, it will read and return /etc/issue.
34
+ # The return type is String.
35
+ # If none of the info is available, it will return an empty frozen String.
36
+ def distribution
37
+ @@distribution ||= if os_release.key?(:NAME)
38
+ os_release[:NAME]
39
+ else
40
+ v = lsb_release
41
+
42
+ if v.key?(:DISTRIB_DESCRIPTION)
43
+ v[:DISTRIB_DESCRIPTION]
44
+ elsif v.key?(:DISTRIB_ID)
45
+ v[:DISTRIB_ID]
46
+ elsif File.readable?('/etc/issue')
47
+ IO.read('/etc/issue').strip
48
+ else
49
+ 'Unknown'.freeze
50
+ end
51
+ end
52
+ end
53
+
54
+ # Reads /etc/hostname and returns the hostname.
55
+ # The return type is String.
56
+ # If the info info isn't available, it will return 'localhost'.
57
+ def hostname
58
+ @@hostname ||= if File.exist?('/etc/hostname')
59
+ IO.read('/etc/hostname').strip
60
+ else
61
+ 'localhost'
62
+ end
63
+ end
64
+
65
+ # Reads ruby configuration and tries to guess if the system is 32 bit or 64 bit.
66
+ # The return type is Integer.
67
+ def bits
68
+ @@bits ||= if RbConfig::CONFIG['host_cpu'].end_with?('64') || RUBY_PLATFORM[/x86_64/]
69
+ 64
70
+ else
71
+ 32
72
+ end
73
+ end
74
+
75
+ # Reads /proc/uptime and returns the system uptime:
76
+ # {:hour=>10, :minute=>34, :second=>12.59}
77
+ #
78
+ # If the stat isn't available, an empty hash is returned.
14
79
  def uptime
15
- raise StatUnavailable, 'Cannot read /proc/uptime' unless uptime_readable?
80
+ return {} unless uptime_readable?
16
81
 
17
82
  uptime = IO.read('/proc/uptime').to_f
18
83
  uptime_i = uptime.to_i
@@ -30,7 +95,7 @@ module LinuxStat
30
95
 
31
96
  private
32
97
  def release(file)
33
- IO.readlines(file).reduce({}) { |h, x|
98
+ IO.readlines(file, 4096).reduce({}) { |h, x|
34
99
  x.strip!
35
100
  next h if x.empty?
36
101
 
@@ -39,8 +104,10 @@ module LinuxStat
39
104
  key = splitted[0].to_s.strip
40
105
  value = splitted[1..-1].join(?=).to_s.strip
41
106
 
42
- dumped = value[0] == ?" && value[-1] == ?"
43
- h.merge!( key.to_sym => dumped ? value.undump : value )
107
+ value[0] = '' if value[0] == ?"
108
+ value[-1] = '' if value[-1] == ?"
109
+
110
+ h.merge!( key.to_sym => value )
44
111
  }
45
112
  end
46
113