lib/facter/resolvers/aix/ffi/ffi_helper.rb
# frozen_string_literal: true
require 'ffi'
require_relative 'structs'
require_relative 'ffi'
module Facter
module Resolvers
module Aix
module FfiHelper
KINFO_GET_AVENRUN = 1
KINFO_READ = 8 << 8
KINFO_RT = 1 << 8
KINFO_RT_IFLIST = KINFO_RT | 3
module Libc
extend ::FFI::Library
RTLD_LAZY = 0x00000004
RTLD_GLOBAL = 0x00010000
RTLD_MEMBER = 0x00040000
@ffi_lib_flags = RTLD_LAZY | RTLD_GLOBAL | RTLD_MEMBER
ffi_lib 'libc.a(shr.o)'
attach_function :getkerninfo, %i[int pointer pointer int], :int
attach_function :inet_ntop, %i[int pointer pointer uint], :string
end
def self.read_load_averages
averages = ::FFI::MemoryPointer.new(:long_long, 3)
averages_size = ::FFI::MemoryPointer.new(:int, 1)
averages_size.write_int(averages.size)
return if Libc.getkerninfo(KINFO_READ | KINFO_GET_AVENRUN, averages, averages_size, 0).negative?
averages.read_array_of_long_long(3).map { |x| (x / 65_536.0) }
end
def self.read_interfaces
ksize = Libc.getkerninfo(KINFO_RT_IFLIST, nil, nil, 0)
log.debug('getkerninfo call was unsuccessful') if ksize.zero?
ksize_ptr = ::FFI::MemoryPointer.new(:int, ksize.size)
ksize_ptr.write_int(ksize)
result_ptr = ::FFI::MemoryPointer.new(:char, ksize)
res = Libc.getkerninfo(KINFO_RT_IFLIST, result_ptr, ksize_ptr, 0)
log.debug('getkerninfo call was unsuccessful') if res == -1
cursor = 0
interfaces = {}
while cursor < ksize_ptr.read_int
hdr = FFI::IfMsghdr.new(result_ptr + cursor)
case hdr[:ifm_type]
when FFI::RTM_IFINFO
link_addr = FFI::SockaddrDl.new(hdr.to_ptr + hdr.size)
interface_name = link_addr[:sdl_data].to_s[0, link_addr[:sdl_nlen]]
interfaces[interface_name] ||= {}
when FFI::RTM_NEWADDR
addresses = {}
addr_cursor = cursor + hdr.size
FFI::RTAX_LIST.each do |key|
xand = hdr[:ifm_addrs] & FFI::RTA_LIST[key]
next unless xand != 0
sockaddr = FFI::Sockaddr.new(result_ptr + addr_cursor)
addresses[key] = sockaddr
roundup_nr = roundup(sockaddr)
addr_cursor += roundup_nr
end
family = FFI::AF_UNSPEC
addresses.each_value do |addr|
if family != FFI::AF_UNSPEC &&
addr[:sa_family] != FFI::AF_UNSPEC &&
family != addr[:sa_family]
family = FFI::AF_MAX
break
end
family = addr[:sa_family]
end
if addresses[FFI::RTAX_NETMASK][:sa_len]
addresses[FFI::RTAX_NETMASK][:sa_family] = family
netmask = address_to_string(addresses[FFI::RTAX_NETMASK])
end
address = address_to_string(addresses[FFI::RTAX_IFA]) if addresses[FFI::RTAX_IFA][:sa_len]
if addresses[FFI::RTAX_NETMASK][:sa_len] && addresses[FFI::RTAX_IFA][:sa_len]
network = address_to_string(addresses[FFI::RTAX_IFA], addresses[FFI::RTAX_NETMASK])
end
bindings = family == FFI::AF_INET ? :bindings : :bindings6
interfaces[interface_name][bindings] ||= []
interfaces[interface_name][bindings] << {
netmask: netmask.read_string,
address: address.read_string,
network: network.read_string
}
else
log.debug("got an unknown RT_IFLIST message: #{hdr[:ifm_type]}")
end
cursor += hdr[:ifm_msglen]
end
interfaces
end
def self.roundup(sockaddr)
if sockaddr[:sa_len].positive?
1 + ((sockaddr[:sa_len] - 1) | (1.size - 1))
else
1.size
end
end
def self.address_to_string(sockaddr, mask = nil)
if sockaddr[:sa_family] == FFI::AF_INET
in_addr_ip = FFI::SockaddrIn.new(sockaddr.to_ptr)
if mask && sockaddr[:sa_family] == mask[:sa_family]
in_addr_mask = FFI::SockaddrIn.new(mask.to_ptr)
in_addr_ip[:sin_addr][:s_addr] &= in_addr_mask[:sin_addr][:s_addr]
end
buffer = ::FFI::MemoryPointer.new(:char, FFI::INET_ADDRSTRLEN)
Libc.inet_ntop(FFI::AF_INET, in_addr_ip[:sin_addr].to_ptr, buffer, 16)
buffer
elsif sockaddr[:sa_family] == FFI::AF_INET6
in_addr_ip = FFI::SockaddrIn6.new(sockaddr.to_ptr)
if mask && sockaddr[:sa_family] == mask[:sa_family]
in_addr_mask = FFI::SockaddrIn6.new(sockaddr.to_ptr)
16.times do |i|
in_addr_ip[:sin6_addr][:u6_addr8][i] &= in_addr_mask[:sin6_addr][:u6_addr8][i]
end
end
buffer = ::FFI::MemoryPointer.new(:char, FFI::INET6_ADDRSTRLEN)
Libc.inet_ntop(FFI::AF_INET6, in_addr_ip[:sin6_addr].to_ptr, buffer, 16)
buffer
end
end
def self.log
@log ||= Log.new(self)
end
end
end
end
end