# frozen_string_literal: true require 'ffi' require 'rbconfig' require 'ostruct' # The Sys module serves as a namespace only. module Sys # The Uname class encapsulates information about the system. class Uname extend FFI::Library ffi_lib FFI::Library::LIBC # Error raised if the uname() function fails. class Error < StandardError; end # :stopdoc # Buffer size for uname struct char arrays case RbConfig::CONFIG['host_os'] when /linux/i BUFSIZE = 65 when /bsd|dragonfly/i BUFSIZE = 32 else BUFSIZE = 256 end attach_function :uname_c, :uname, [:pointer], :int private_class_method :uname_c begin attach_function :sysctl, %i[pointer uint pointer pointer pointer size_t], :int private_class_method :sysctl CTL_HW = 6 # Generic hardware/cpu HW_MODEL = 2 # Specific machine model rescue FFI::NotFoundError # Ignore. Not suppored. end begin attach_function :sysinfo, %i[int pointer long], :long private_class_method :sysinfo SI_SYSNAME = 1 # OS name SI_HOSTNAME = 2 # Node name SI_RELEASE = 3 # Operating system release SI_VERSION = 4 # Version field of utsname SI_MACHINE = 5 # Machine type SI_ARCHITECTURE = 6 # Instruction set architecture SI_HW_SERIAL = 7 # Hardware serial number SI_HW_PROVIDER = 8 # Hardware manufacturer SI_SRPC_DOMAIN = 9 # Secure RPC domain SI_PLATFORM = 513 # Platform identifier SI_ISALIST = 514 # Supported isalist SI_DHCP_CACHE = 515 # Kernel cached DHCPACK rescue FFI::NotFoundError # Ignore. Not suppored. end # FFI class passed to the underlying C uname function. class UnameFFIStruct < FFI::Struct members = [ :sysname, [:char, BUFSIZE], :nodename, [:char, BUFSIZE], :release, [:char, BUFSIZE], :version, [:char, BUFSIZE], :machine, [:char, BUFSIZE] ] members.push(:domainname, [:char, BUFSIZE]) if RbConfig::CONFIG['host_os'] =~ /linux/i members.push(:__id_number, [:char, BUFSIZE]) if RbConfig::CONFIG['host_os'] =~ /hpux/i layout(*members) end fields = %w[ sysname nodename release version machine ] fields.push('domainname') if RbConfig::CONFIG['host_os'] =~ /linux/i fields.push('id_number') if RbConfig::CONFIG['host_os'] =~ /hpux/i fields.push('model') if RbConfig::CONFIG['host_os'] =~ /darwin|bsd|dragonfly/i private_constant :UnameFFIStruct # :startdoc: UnameStruct = Struct.new('UnameStruct', *fields) # Returns a struct that contains the sysname, nodename, machine, version # and release of your system. # # On OS X and BSD platforms it will also include the model. # # On HP-UX, it will also include the id_number. # # Example: # # require 'sys/uname' # # p Sys::Uname.uname # def self.uname utsname = UnameFFIStruct.new raise Error, 'uname() function call failed' if uname_c(utsname) < 0 struct = UnameStruct.new struct[:sysname] = utsname[:sysname].to_s struct[:nodename] = utsname[:nodename].to_s struct[:release] = utsname[:release].to_s struct[:version] = utsname[:version].to_s struct[:machine] = utsname[:machine].to_s struct[:model] = get_model() if RbConfig::CONFIG['host_os'] =~ /darwin|bsd|dragonfly/i struct[:id_number] = utsname[:__id_number].to_s if RbConfig::CONFIG['host_os'] =~ /hpux/i struct[:domainname] = utsname[:domainname].to_s if RbConfig::CONFIG['host_os'] =~ /linux/i # Let's add a members method that works for testing and compatibility if struct.members.nil? struct.instance_eval <<-RUBY, __FILE__, __LINE__ + 1 def members @table.keys.map(&:to_s) end RUBY end struct.freeze end # Returns the name of this implementation of the operating system. # # Example: # # Uname.sysname # => 'Darwin' # def self.sysname uname.sysname end # Returns the name of this node within the communications network to # which this node is attached, if any. This is often, but not # necessarily, the same as the host name. # # Example: # # Uname.nodename # => 'your_host.foo.com' # def self.nodename uname.nodename end # Returns the current release level of your operating system. # # Example: # # Uname.release # => '2.2.16-3' # def self.release uname.release end # Returns the current version level of your operating system. # # Example: # # Uname.version # => '5.9' # def self.version uname.version end # Returns the machine hardware type. # # Example: # # Uname.machine # => 'i686' # def self.machine uname.machine end if defined? :sysctl # Returns the model type. # # Example: # # Uname.model # => 'MacBookPro5,3' # def self.model uname.model end end if defined? :sysinfo # The basic instruction set architecture of the current # system, e.g. sparc, i386, etc. # def self.architecture uname.architecture end # The specific model of the hardware platform, e.g Sun-Blade-1500, etc. # def self.platform uname.platform end # The string consisting of the ASCII hexidecimal encoding of the name # of the interface configured by boot(1M) followed by the DHCPACK reply # from the server. # def self.dhcp_cache uname.dhcp_cache end # The variant instruction set architectures executable on the # current system. # def self.isa_list uname.isa_list end # The ASCII representation of the hardware-specific serial number # of the physical machine on which the function is executed. # def self.hw_serial uname.hw_serial.to_i end # The name of the of the hardware provider. # def self.hw_provider uname.hw_provider end # The Secure Remote Procedure Call domain name. # def self.srpc_domain uname.srpc_domain end end private # Returns the model for systems that define sysctl(). # def self.get_model buf = 0.chr * BUFSIZE mib = FFI::MemoryPointer.new(:int, 2).write_array_of_int([CTL_HW, HW_MODEL]) size = FFI::MemoryPointer.new(:long, 1).write_int(buf.size) sysctl(mib, 2, buf, size, nil, 0) buf.strip end private_class_method :get_model end end