linux_stat 0.1.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ unless (have_header('sys/statvfs.h') && have_header('ruby.h'))
4
+ abort('Missing header')
5
+ end
6
+
7
+ create_makefile 'linux_stat/fs_stat'
@@ -0,0 +1,28 @@
1
+ #include <sys/statvfs.h>
2
+ #include "ruby.h"
3
+
4
+ static VALUE statfs(VALUE obj, VALUE dir) {
5
+ struct statvfs buf ;
6
+ char *d = StringValuePtr(dir) ;
7
+ VALUE hash = rb_hash_new() ;
8
+
9
+ if(statvfs(d, &buf) < 0) return hash ;
10
+
11
+ rb_hash_aset(hash, ID2SYM(rb_intern("block_size")), INT2NUM(buf.f_bsize)) ;
12
+ rb_hash_aset(hash, ID2SYM(rb_intern("fragment_size")), INT2NUM(buf.f_frsize)) ;
13
+ rb_hash_aset(hash, ID2SYM(rb_intern("blocks")), INT2NUM(buf.f_blocks)) ;
14
+ rb_hash_aset(hash, ID2SYM(rb_intern("block_free")), INT2NUM(buf.f_bfree)) ;
15
+ rb_hash_aset(hash, ID2SYM(rb_intern("block_avail_unpriv")), INT2NUM(buf.f_bavail)) ;
16
+ rb_hash_aset(hash, ID2SYM(rb_intern("inodes")), INT2NUM(buf.f_files)) ;
17
+ rb_hash_aset(hash, ID2SYM(rb_intern("free_inodes")), INT2NUM(buf.f_ffree)) ;
18
+ rb_hash_aset(hash, ID2SYM(rb_intern("filesystem_id")), INT2NUM(buf.f_fsid)) ;
19
+ rb_hash_aset(hash, ID2SYM(rb_intern("mount_flags")), INT2NUM(buf.f_flag)) ;
20
+ rb_hash_aset(hash, ID2SYM(rb_intern("max_filename_length")), INT2NUM(buf.f_namemax)) ;
21
+
22
+ return hash ;
23
+ }
24
+
25
+ void Init_fs_stat() {
26
+ VALUE fs = rb_define_module("FS") ;
27
+ rb_define_module_function(fs, "stat", statfs, 1) ;
28
+ }
@@ -1,10 +1,4 @@
1
1
  require "linux_stat/version"
2
-
3
- module LinuxStat
4
- class StatUnavailable < StandardError
5
- end
6
- end
7
-
8
2
  require "linux_stat/battery"
9
3
  require "linux_stat/bios"
10
4
  require "linux_stat/cpu"
@@ -14,3 +8,6 @@ require "linux_stat/net"
14
8
  require "linux_stat/os"
15
9
  require "linux_stat/process"
16
10
  require "linux_stat/swap"
11
+ require "linux_stat/mounts"
12
+ require "linux_stat/fs_stat"
13
+ require "linux_stat/filesystem"
@@ -3,10 +3,13 @@ module LinuxStat
3
3
  PATH = "/sys/class/power_supply/BAT0"
4
4
 
5
5
  class << self
6
+ # Returns true or false based on the presence of the battery.
6
7
  def present?
7
8
  @@present ||= Dir.exist?(PATH)
8
9
  end
9
10
 
11
+ # Returns the details of the battery.
12
+ #If the battery is not present it will return an empty Hash.
10
13
  def stat
11
14
  st = status.downcase
12
15
  return {} unless present?
@@ -23,40 +26,60 @@ module LinuxStat
23
26
  }
24
27
  end
25
28
 
29
+ # Returns the model of the battery.
30
+ #If the battery is not present or the information isn't available it will return an empty String.
26
31
  def model
27
32
  return ''.freeze unless model_readable?
28
33
  IO.read(File.join(PATH, 'model_name')).tap(&:strip!)
29
34
  end
30
35
 
36
+ # Returns the manufacturer of the battery.
37
+ # If the battery is not present or the information is not available, it will return an empty String.
31
38
  def manufacturer
32
39
  return ''.freeze unless manufacturer_readable?
33
40
  IO.read(File.join(PATH, 'manufacturer')).tap(&:strip!)
34
41
  end
35
42
 
43
+ # Returns the technology of the battery.
44
+ # If the battery is not present or the information is not available, it will return an empty String.
36
45
  def technology
37
46
  return ''.freeze unless tech_readable?
38
47
  IO.read(File.join(PATH, 'technology')).tap(&:strip!)
39
48
  end
40
49
 
50
+ # Returns the status of the battery.
51
+ # If the battery is not present or the information is not available, it will return an empty String.
52
+ # The status generally includes either of the full, charging, discharging and unknown states in most cases.
41
53
  def status
42
54
  return ''.freeze unless status_readable?
43
55
  IO.read(File.join(PATH, 'status')).tap(&:strip!)
44
56
  end
45
57
 
58
+ # Returns true if the battery is charging, false if the battery is not charging.
59
+ # If the battery is not present or the information is not available, it will return nil.
46
60
  def charging?
61
+ return nil if status.empty?
47
62
  %w(full charging unknown).each(&:freeze).include?(status.downcase)
48
63
  end
49
64
 
65
+ # Returns true if the battery is discharging, false if the battery is not discharging.
66
+ # If the battery is not present or the information is not available, it will return nil.
50
67
  def discharging?
68
+ return nil if status.empty?
51
69
  status.downcase == 'discharging'
52
70
  end
53
71
 
72
+ # Returns true if the battery status if full, false if the battery status is not full.
73
+ # If the battery is not present or the information is not available, it will return nil.
54
74
  def full?
75
+ return nil if status.empty?
55
76
  status.downcase == 'full'
56
77
  end
57
78
 
79
+ # Returns the charge of the battery.
80
+ # If the battery is not present or the information is not available, it will return nil.
58
81
  def charge
59
- return 0.0 unless charge_now_readable?
82
+ return nil unless charge_now_readable?
60
83
  charge_now = IO.read(File.join(PATH, 'charge_now')).to_i
61
84
  charge_full = IO.read(File.join(PATH, 'charge_full')).to_i
62
85
 
@@ -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,17 @@
1
1
  module LinuxStat
2
2
  module CPU
3
3
  class << self
4
+ # stat(sleep = 0.075)
5
+ # Where sleep is the delay to gather the data.
6
+ # This method returns the cpu usage of all threads.
7
+ #
8
+ # The first one is aggregated CPU usage reported by the Linux kernel.
9
+ # And the consecutive ones are the real core usages.
10
+ #
11
+ # On a system with 4 threads, the output will be like::
12
+ # {0=>84.38, 1=>100.0, 2=>50.0, 3=>87.5, 4=>87.5}
13
+ #
14
+ # If the information is not available, it will return an empty Hash
4
15
  def stat(sleep = 0.075)
5
16
  return {} unless stat?
6
17
 
@@ -21,8 +32,15 @@ module LinuxStat
21
32
  end
22
33
  end
23
34
 
35
+ # total_usage(sleep = 0.075)
36
+ # Where sleep is the delay to gather the data.
37
+ # This method returns the cpu usage of all threads.
38
+ #
39
+ # 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.
40
+ #
41
+ # If the information is not available, it will return nil.
24
42
  def total_usage(sleep = 0.075)
25
- return {} unless stat?
43
+ return nil unless stat?
26
44
 
27
45
  data = IO.foreach('/proc/stat').first.split.tap(&:shift).map!(&:to_f)
28
46
  sleep(sleep)
@@ -36,33 +54,44 @@ module LinuxStat
36
54
  totald.-(idle_now - idle_then).fdiv(totald).*(100).round(2).abs
37
55
  end
38
56
 
57
+ # Returns the total number of CPU threads.
58
+ # If the information isn't available, it will return 0.
39
59
  def count
40
60
  # CPU count can change during the program runtime
41
61
  cpuinfo.count { |x| x.start_with?('processor') }
42
62
  end
43
63
 
64
+ # Returns the model of processor.
65
+ # If the information isn't available, it will return en empty string.
66
+ # The output is also cached ; as changing the value in runtime is unexpected.
44
67
  def model
45
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
46
- # to add/remove CPUs during program runtime
47
68
  @@name ||= cpuinfo.find { |x| x.start_with?('model name') }.to_s.split(?:)[-1].to_s.strip
48
69
  end
49
70
 
50
- # Returns an array with current core frequencies corresponding to the usages
71
+ # Returns an array with current core frequencies corresponding to the usages.
72
+ # If the information isn't available, it will return an empty array.
51
73
  def cur_freq
52
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
53
- # to add/remove CPUs during program runtime
54
-
55
74
  @@cpu_freqs ||= Dir["/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq"]
56
- @@cpu_freqs.map { |x| IO.read(x).to_i }
75
+ @@cur_freqs_readable ||= @@cpu_freqs.all?(&File.method(:readable?))
76
+
77
+ if @@cur_freqs_readable
78
+ @@cpu_freqs.map { |x| IO.read(x).to_i }
79
+ else
80
+ []
81
+ end
57
82
  end
58
83
 
59
- # Returns an array with max core frequencies corresponding to the usages
84
+ # Returns an array with max core frequencies corresponding to the usages.
85
+ # If the information isn't available, it will return an empty array.
60
86
  def max_freq
61
- # Cached ; as changing the value in runtime is unexpected ; nobody is going
62
- # to add/remove CPUs during program runtime
63
-
64
87
  @@max_freqs ||= Dir["/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_max_freq"]
65
- @@max_freqs.map { |x| IO.read(x).to_i }
88
+ @@max_freqs_readable ||= @@max_freqs.all?(&File.method(:readable?))
89
+
90
+ if @@max_freqs_readable
91
+ @@max_freqs.map { |x| IO.read(x).to_i }
92
+ else
93
+ []
94
+ end
66
95
  end
67
96
 
68
97
  alias usages stat
@@ -0,0 +1,96 @@
1
+ # require 'linux_stat/fs_stat'
2
+
3
+ module LinuxStat
4
+ module Filesystem
5
+ extend FS
6
+
7
+ class << self
8
+ # stat(fs = '/')
9
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
10
+ #
11
+ # It returns a Hash with the following info:
12
+ # 1. total size of the device (in bytes)
13
+ # 2. free space (in kilobytes)
14
+ # 3. used space (in kilobytes)
15
+ #
16
+ # In a hash format:
17
+ # {:total=>119981191168, :free=>43155574784, :used=>76825616384, :available=>43155574784}
18
+ #
19
+ # If the stat can't be acquired, this method will return an empty Hash.
20
+ def stat(fs = ?/.freeze)
21
+ s = stat_raw(fs)
22
+ return {} if s.empty?
23
+ s.default = 0
24
+
25
+ {
26
+ total: s[:block_size] * s[:blocks],
27
+ free: s[:block_size] * s[:block_free],
28
+ used: s[:blocks].-(s[:block_free]) * s[:block_size],
29
+ }
30
+ end
31
+
32
+ # stat(fs = '/')
33
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
34
+ # It returns the total size of a given disk in bytes.
35
+ #
36
+ # If the stat can't be acquired, this method will return nil.
37
+ def total(fs = ?/.freeze)
38
+ s = stat_raw(fs)
39
+ return nil if s.empty?
40
+ s.default = 0
41
+ s[:block_size] * s[:blocks]
42
+ end
43
+
44
+ # stat(fs = '/')
45
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
46
+ # It returns the total free space in a disk in bytes.
47
+ # It is to be noted that free is not same as available.
48
+ # Free returns the size of free blocks.
49
+ #
50
+ # If the stat can't be acquired, this method will return an empty Hash.
51
+ def free(fs = ?/.freeze)
52
+ s = stat_raw(fs)
53
+ return nil if s.empty?
54
+ s.default = 0
55
+ s[:block_size] * s[:block_free]
56
+ end
57
+
58
+ # stat(fs = '/')
59
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
60
+ # It returns the used space of a given disk in bytes.
61
+ #
62
+ # If the stat can't be acquired, this method will return nil.
63
+ def used(fs = ?/.freeze)
64
+ s = stat_raw(fs)
65
+ return nil if s.empty?
66
+ s.default = 0
67
+ s[:blocks].-(s[:block_free]) * s[:block_size]
68
+ end
69
+
70
+ # stat(fs = '/')
71
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
72
+ # It returns the total free space in a disk in bytes.
73
+ # It is to be noted that free is not same as available.
74
+ # Available returns the size of free blocks for unpriviledged users.
75
+ #
76
+ # If the stat can't be acquired, this method will return an empty Hash.
77
+ def available(fs = ?/.freeze)
78
+ s = stat_raw(fs)
79
+ return nil if s.empty?
80
+ s.default = 0
81
+ s[:block_size] * s[:block_avail_unpriv]
82
+ end
83
+
84
+ # stat(fs = '/')
85
+ # Where fs is the directory of the file system (like / or /tmp/ or /run/media/thumbdrive).
86
+ #
87
+ # It returns a Hash with the following data (for example):
88
+ # {:block_size=>4096, :fragment_size=>4096, :blocks=>29292283, :block_free=>10535967, :block_avail_unpriv=>10535967, :inodes=>58612160, :free_inodes=>56718550, :filesystem_id=>2050, :mount_flags=>1024, :max_filename_length=>255}
89
+ #
90
+ # If the stat can't be acquired, this method will return an empty Hash.
91
+ def stat_raw(fs = '/'.freeze)
92
+ FS.stat(fs)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,24 +1,33 @@
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
8
  return ''.freeze if string.empty?
6
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.
9
15
  def build_user
10
16
  @@build_user ||= string.split(/(\(.+\))/).each(&:strip!)
11
17
  .reject(&:empty?).find { |x| x[/^\(.+\)$/] }.to_s
12
- .split[0].to_s[1..-2].to_s
18
+ .split[0].to_s[1..-2].to_s.freeze
13
19
  end
14
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.
15
24
  def compiler
16
25
  return ''.freeze if string.empty?
17
26
 
18
27
  @@compiler ||= string.split(/(\(.+\))/).each(&:strip!)
19
28
  .reject(&:empty?)
20
29
  .find { |x| x[/^\(.+\)$/] }.to_s
21
- .split.find { |x| !x[/^(.+@.+)$/] }.to_s[/\w+/].to_s
30
+ .split.find { |x| !x[/^(.+@.+)$/] }.to_s[/\w+/].to_s.freeze
22
31
 
23
32
  @@compiler_val ||= case @@compiler
24
33
  when /gcc/i then [:gcc ]
@@ -28,32 +37,106 @@ module LinuxStat
28
37
  end << compiler_version
29
38
  end
30
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.
31
43
  def compiler_version
32
44
  @@compiler_version ||= string.split(/(\(.+?\))/).each(&:strip!)
33
45
  .reject(&:empty?)[2..4].to_a
34
- .find { |x| x[/[\d.]+/] }.to_s[/[\d.]+/].to_s
46
+ .find { |x| x[/[\d.]+/] }.to_s[/[\d.]+/].to_s.freeze
35
47
  end
36
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.
37
66
  def build_date
38
- return @@time ||= Time.new(0) if string.empty?
67
+ return nil if splitted.empty?
39
68
 
40
69
  @@time ||= begin
41
- require 'time'
70
+ require 'time' unless Time.respond_to?(:strptime)
42
71
 
43
- time = splitted.each_slice(8).find do |x|
44
- x.each(&:strip!)
45
- p Time.strptime(x.join(?\s.freeze), '%d %b %Y %H:%M:%S %z'.freeze) rescue nil
46
- end
72
+ splitted.each_cons(5).map do |x|
73
+ joined = x.each(&:strip!).join(?\s.freeze)
47
74
 
48
- time ? Time.strptime(time.join(?\s.freeze), "%d %b %Y %H:%M:%S %z") : Time.new(0)
75
+ # Match 21 Oct 2020 01:11:20 +0000
76
+ if joined[/^\d{1,2}\s\w{3}\s\d{4}\s\d{1,2}:\d{1,2}:\d{1,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{1,2}\s\d{1,2}:\d{1,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{1,2}-\d{1,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]
91
+ end
92
+ end
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{1,2}\s\w{3}\s\d{4}\s\d{1,2}:\d{1,2}:\d{1,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{1,2}\s\d{1,2}:\d{1,2}:\d{1,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{1,2}-\d{1,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]
49
133
  end
50
134
  end
51
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.
52
138
  def string
53
- # Cached ; as changing the value in runtime is unexpected
54
- # Hotfix update can be problem, but it's rare and might not
55
- # affect the version string during program runtime.
56
- @@string ||= File.readable?('/proc/version') ? IO.read('/proc/version', 1000).tap(&:strip!) : ''
139
+ @@string ||= File.readable?('/proc/version') ? IO.read('/proc/version', 1024).tap(&:strip!) : ''
57
140
  end
58
141
 
59
142
  private