Created
September 24, 2020 19:11
-
-
Save logicminds/1672ee9d715bf97167ffe83667f91424 to your computer and use it in GitHub Desktop.
Revamped sysinfo gem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'socket' | |
require 'time' | |
require 'tmpdir' | |
require 'json' | |
# = SysInfo | |
# | |
# A container for the platform specific system information. | |
# Portions of this code were originally from Amazon's EC2 AMI tools, | |
# specifically lib/platform.rb. | |
SysInfoTable = Struct.new( | |
:vm, | |
:os, | |
:impl, | |
:arch, | |
:hostname, | |
:ipaddress_internal, | |
:ipaddress_external, | |
:uptime, | |
:paths, | |
:tmpdir, | |
:home, | |
:shell, | |
:user, | |
:ruby, | |
keyword_init: true | |
) | |
class SysInfo < SysInfoTable | |
unless defined?(IMPLEMENTATIONS) | |
IMPLEMENTATIONS = [ | |
# These are for JRuby, System.getproperty('os.name'). | |
# For a list of all values, see: http://lopica.sourceforge.net/os.html | |
# regexp matcher os implementation | |
[/mac\s*os\s*x/i, :unix, :osx], | |
[/sunos/i, :unix, :solaris], | |
[/windows\s*ce/i, :windows, :wince], | |
[/windows/i, :windows, :windows], | |
[/osx/i, :unix, :osx], | |
# These are for RUBY_PLATFORM and JRuby | |
[/java/i, :java, :java], | |
[/darwin/i, :unix, :osx], | |
[/linux/i, :unix, :linux], | |
[/freebsd/i, :unix, :freebsd], | |
[/netbsd/i, :unix, :netbsd], | |
[/solaris/i, :unix, :solaris], | |
[/irix/i, :unix, :irix], | |
[/cygwin/i, :unix, :cygwin], | |
[/mswin/i, :windows, :windows], | |
[/djgpp/i, :windows, :djgpp], | |
[/mingw/i, :windows, :mingw], | |
[/bccwin/i, :windows, :bccwin], | |
[/wince/i, :windows, :wince], | |
[/vms/i, :vms, :vms], | |
[/os2/i, :os2, :os2], | |
[nil, :unknown, :unknown] | |
].freeze | |
ARCHITECTURES = [ | |
[/(i\d86)/i, :x86], | |
[/x86_64/i, :x86_64], | |
[/x86/i, :x86], # JRuby | |
[/ia64/i, :ia64], | |
[/alpha/i, :alpha], | |
[/sparc/i, :sparc], | |
[/mips/i, :mips], | |
[/powerpc/i, :powerpc], | |
[/universal/i, :x86_64], | |
[nil, :unknown] | |
].freeze | |
end | |
alias implementation impl | |
alias architecture arch | |
def initialize | |
@vm, @os, @impl, @arch = find_platform_info | |
@hostname, @ipaddress_internal, @uptime = find_network_info | |
@ruby = RUBY_VERSION.split('.').collect(&:to_i) | |
@user = getpwattr(:name) || ENV['USER'] | |
require 'Win32API' if @os == :windows && @vm == :ruby | |
end | |
# Returns [vm, os, impl, arch] | |
def find_platform_info | |
vm = :ruby | |
os = :unknown | |
impl = :unknown | |
arch = :unknow | |
IMPLEMENTATIONS.each do |r, o, i| | |
next unless RUBY_PLATFORM =~ r | |
os = o | |
impl = i | |
break | |
end | |
ARCHITECTURES.each do |r, a| | |
next unless RUBY_PLATFORM =~ r | |
arch = a | |
break | |
end | |
os == :java ? guess_java : [vm, os, impl, arch] | |
end | |
# Returns [hostname, ipaddr (internal), uptime] | |
def find_network_info | |
hostname = :unknown | |
ipaddr = :unknown | |
uptime = :unknown | |
begin | |
hostname = find_hostname | |
ipaddr = find_ipaddress_internal | |
uptime = find_uptime | |
rescue StandardError => e # Be silent! | |
end | |
[hostname, ipaddr, uptime] | |
end | |
# Return the hostname for the local machine | |
def find_hostname | |
Socket.gethostname | |
end | |
# Returns the local uptime in hours. Use Win32API in Windows, | |
# 'sysctl -b kern.boottime' os osx, and 'who -b' on unix. | |
# Based on Ruby Quiz solutions by: Matthias Reitinger | |
# On Windows, see also: net statistics server | |
def find_uptime | |
hours = 0 | |
begin | |
seconds = execute_platform_specific('find_uptime') || 0 | |
hours = seconds / 3600 # seconds to hours | |
rescue StandardError => e | |
# puts ex.message # TODO: implement debug? | |
end | |
hours | |
end | |
# Return the local IP address which receives external traffic | |
# from: http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/ | |
# NOTE: This <em>does not</em> open a connection to the IP address. | |
def find_ipaddress_internal | |
# turn off reverse DNS resolution temporarily | |
orig = Socket.do_not_reverse_lookup | |
Socket.do_not_reverse_lookup = true | |
UDPSocket.open { |s| s.connect('65.74.177.129', 1); s.addr.last } # GitHub IP | |
ensure | |
Socket.do_not_reverse_lookup = orig | |
end | |
# Returns a String of the full platform descriptor in the format: VM-OS-IMPL-ARCH | |
# e.g. <tt>java-unix-osx-x86_64</tt> | |
def platform | |
"#{@vm}-#{@os}-#{@impl}-#{@arch}" | |
end | |
# Returns the environment paths as an Array | |
def paths | |
execute_platform_specific(:paths) | |
end | |
# Returns the path to the current user's home directory | |
def home | |
execute_platform_specific(:home) | |
end | |
# Returns the name of the current shell | |
def shell | |
execute_platform_specific(:shell) | |
end | |
# Returns the path to the current temp directory | |
def tmpdir | |
execute_platform_specific(:tmpdir) | |
end | |
private | |
# Look for and execute a platform specific method. | |
# The name of the method will be in the format: +dtype-VM-OS-IMPL+. | |
# e.g. find_uptime_ruby_unix_osx | |
# | |
def execute_platform_specific(dtype) | |
criteria = [@vm, @os, @impl] | |
until criteria.empty? | |
meth = [dtype, criteria].join('_').to_sym | |
return send(meth) if SysInfo.private_method_defined?(meth) | |
criteria.pop | |
end | |
raise "#{dtype}_#{@vm}_#{@os}_#{@impl} not implemented" | |
end | |
def paths_ruby_unix | |
(ENV['PATH'] || '').split(':') | |
end | |
# Not tested! | |
def paths_ruby_windows | |
(ENV['PATH'] || '').split(';') | |
end | |
def paths_java | |
delim = @impl == :windows ? ';' : ':' | |
(ENV['PATH'] || '').split(delim) | |
end | |
def tmpdir_ruby_unix | |
Dir.tmpdir || '/tmp' | |
end | |
def tmpdir_ruby_windows | |
Dir.tmpdir || 'C:\\temp' | |
end | |
def tmpdir_java | |
default = @impl == :windows ? 'C:\\temp' : '/tmp' | |
(Dir.tmpdir || default) | |
end | |
def shell_ruby_unix | |
(ENV['SHELL'] || getpwattr(:shell) || 'bash').to_sym | |
end | |
def shell_ruby_windows | |
:dos | |
end | |
alias shell_java_unix shell_ruby_unix | |
alias shell_java_windows shell_ruby_windows | |
def home_ruby_unix | |
File.expand_path(getpwattr(:dir)) | |
end | |
def home_ruby_windows | |
File.expand_path(ENV['USERPROFILE']) | |
end | |
def home_java | |
if @impl == :windows | |
File.expand_path(ENV['USERPROFILE']) | |
else | |
File.expand_path(getpwattr(:dir)) | |
end | |
end | |
# Ya, this is kinda wack. Ruby -> Java -> Kernel32. See: | |
# http://www.oreillynet.com/ruby/blog/2008/01/jruby_meets_the_windows_api_1.html | |
# http://msdn.microsoft.com/en-us/library/ms724408(VS.85).aspx | |
# Ruby 1.9.1: Win32API is now deprecated in favor of using the DL library. | |
def find_uptime_java_windows_windows | |
kernel32 = com.sun.jna.NativeLibrary.getInstance('kernel32') | |
buf = java.nio.ByteBuffer.allocate(256) | |
(kernel32.getFunction('GetTickCount').invokeInt([256, buf].to_java).to_f / 1000).to_f | |
end | |
def find_uptime_ruby_windows_windows | |
# Win32API is required in self.guess | |
getTickCount = Win32API.new('kernel32', 'GetTickCount', nil, 'L') | |
(getTickCount.call.to_f / 1000).to_f | |
end | |
def find_uptime_ruby_unix_osx | |
# This is faster than "who" and could work on BSD also. | |
(Time.now.to_f - Time.at(`sysctl -b kern.boottime 2>/dev/null`.unpack1('L')).to_f).to_f | |
end | |
# This should work for most unix flavours. | |
def find_uptime_ruby_unix | |
# who is sloooooow. Use File.read('/proc/uptime') | |
(Time.now.to_i - Time.parse(`who -b 2>/dev/null`).to_f) | |
end | |
alias find_uptime_java_unix_osx find_uptime_ruby_unix | |
# Determine the values for vm, os, impl, and arch when running on Java. | |
def guess_java | |
vm = :java | |
os = :unknown | |
impl = :unknown | |
arch = :unknown | |
require 'java' | |
include_class java.lang.System unless defined?(System) | |
osname = System.getProperty('os.name') | |
IMPLEMENTATIONS.each do |r, o, i| | |
next unless osname =~ r | |
os = o | |
impl = i | |
break | |
end | |
osarch = System.getProperty('os.arch') | |
ARCHITECTURES.each do |r, a| | |
next unless osarch =~ r | |
arch = a | |
break | |
end | |
[vm, os, impl, arch] | |
end | |
# Returns the local IP address based on the hostname. | |
# According to coderrr (see comments on blog link above), this implementation | |
# doesn't guarantee that it will return the address for the interface external | |
# traffic goes through. It's also possible the hostname isn't resolvable to the | |
# local IP. | |
# | |
# NOTE: This code predates the current ip_address_internal. It was just as well | |
# but the other code is cleaner. I'm keeping this old version here for now. | |
def ip_address_internal_alt | |
ipaddr = :unknown | |
begin | |
saddr = Socket.getaddrinfo(Socket.gethostname, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) | |
ipaddr = saddr.select { |type| type[0] == 'AF_INET' }[0][3] | |
rescue StandardError => e | |
end | |
ipaddr | |
end | |
# Returns a named attribute of the user's /etc/password entry, or nil. | |
# As an example, `getpwdattr(:home)` will return the user's home directory | |
# without relying on ENV['HOME'] being present. | |
def getpwattr(pwattr) | |
passwd = Etc.getpwuid(Process::Sys.getuid) || {} | |
passwd.send(pwattr) | |
end | |
end | |
puts SysInfo.new.dump('json') if $PROGRAM_NAME == __FILE__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment