Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions lib/facter/util/linux.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module Facter
module Util
module Linux
def self.process_running?(process_name)
pidfiles = Dir.glob("{/run,/var/run}/#{process_name}{,*,/}*.pid")
pidfiles.each do |pf|
next unless File.file?(pf)

pid = begin
Integer(Facter::Util::FileHelper.safe_read(pf, '').strip, 10)
rescue StandardError
nil
end
next unless pid&.positive?

begin
# Doesn't actually kill, just detects if the process exists
Process.kill(0, pid)
return true if proc_comm(pid) == process_name || proc_cmdline(pid)&.match?(%r{(^|\s|/)#{process_name}(\s|$)})
rescue Errno::ESRCH
# If we can't confirm identity, still treat it as not running to be safe.
next
rescue Errno::EPERM
# Exists but we can't inspect it; assume it's running.
return true
end
end

# Fallback: Try to find it in /proc
return false unless Dir.exist?('/proc')

Dir.glob('/proc/[0-9]*/comm').any? do |path|
Facter::Util::FileHelper.safe_read(path, nil)&.strip == process_name
end
end

def self.proc_comm(pid)
Facter::Util::FileHelper.safe_read("/proc/#{pid}/comm", nil)&.strip
end

def self.proc_cmdline(pid)
raw = Facter::Util::FileHelper.safe_read("/proc/#{pid}/cmdline", nil)
raw&.tr("\0", ' ')
end
end
end
end
11 changes: 10 additions & 1 deletion lib/facter/util/linux/dhcp.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'facter/util/linux'

module Facter
module Util
module Linux
Expand Down Expand Up @@ -73,11 +75,18 @@ def search_internal_leases(interface_name)
end

def search_with_dhcpcd_command(interface_name)
@log.debug("Attempt to get DHCP for interface #{interface_name}, from dhcpcd command")
return if interface_name == 'lo'

@dhcpcd_command ||= Facter::Core::Execution.which('dhcpcd')
return unless @dhcpcd_command

unless Facter::Util::Linux.process_running?('dhcpcd')
@log.debug('Skipping dhcpcd -U because dhcpcd daemon is not running')
return
end

@log.debug("Attempt to get DHCP for interface #{interface_name}, from dhcpcd command")

output = Facter::Core::Execution.execute("#{@dhcpcd_command} -U #{interface_name}", logger: @log)
dhcp = output.match(/dhcp_server_identifier='(.*)'/)
dhcp[1] if dhcp
Expand Down
28 changes: 28 additions & 0 deletions spec/facter/util/linux/dhcp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
allow(File).to receive(:readable?).with('/var/lib/NetworkManager/').and_return(false)
allow(File).to receive(:readable?).with('/var/db/').and_return(false)

allow(Dir).to receive(:glob).with('{/run,/var/run}/dhcpcd{,*,/}*.pid').and_return(['/run/dhcpcd.pid'])
allow(File).to receive(:file?).with('/run/dhcpcd.pid').and_return(true)
allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/dhcpcd.pid', '').and_return('1234')
allow(Process).to receive(:kill).with(0, 1234).and_return(true)
allow(Facter::Util::FileHelper).to receive(:safe_read).with('/proc/1234/comm', nil).and_return('dhcpcd')

allow(Facter::Core::Execution).to receive(:which)
.with('dhcpcd').and_return('/usr/bin/dhcpcd')
allow(Facter::Core::Execution).to receive(:execute)
Expand Down Expand Up @@ -100,5 +106,27 @@
expect(dhcp_search.dhcp('ens160', 1, log_spy)).to eq(nil)
end
end

context 'when the dhcpcd command is available, but not running' do
before do
allow(Facter::Util::FileHelper).to receive(:safe_read).with('/run/systemd/netif/leases/1', nil).and_return(nil)
allow(File).to receive(:readable?).with('/var/lib/dhclient/').and_return(false)
allow(File).to receive(:readable?).with('/var/lib/dhcp/').and_return(false)
allow(File).to receive(:readable?).with('/var/lib/dhcp3/').and_return(false)
allow(File).to receive(:readable?).with('/var/lib/NetworkManager/').and_return(false)
allow(File).to receive(:readable?).with('/var/db/').and_return(false)
allow(Dir).to receive(:glob).with('{/run,/var/run}/dhcpcd{,*,/}*.pid').and_return([])
allow(Dir).to receive(:glob).with('/proc/[0-9]*/comm').and_return([])

allow(Facter::Core::Execution).to receive(:which)
.with('dhcpcd').and_return('/usr/bin/dhcpcd')

dhcp_search.instance_eval { @dhcpcd_command = nil }
end

it 'returns nil' do
expect(dhcp_search.dhcp('ens160', 1, log_spy)).to eq(nil)
end
end
end
end