# frozen_string_literal: true require 'time' module Facter class UptimeParser SECS_IN_A_DAY = 86_400 SECS_IN_AN_HOUR = 3_600 SECS_IN_A_MINUTE = 60 class << self def uptime_seconds_unix uptime_proc_uptime || uptime_sysctl || uptime_executable end private def uptime_proc_uptime output, _stderr, _status = Open3.capture3("/bin/cat #{uptime_file} 2>/dev/null") output.chomp.split(' ').first.to_i unless output.empty? end def uptime_sysctl output, _stderr, _status = Open3.capture3("sysctl -n #{uptime_sysctl_variable} 2>/dev/null") compute_uptime(Time.at(output.match(/\d+/)[0].to_i)) unless output.empty? end def uptime_executable output, _stderr, _status = Open3.capture3(uptime_executable_cmd + ' 2>/dev/null') return unless output up = 0 output_calculator_methods.find { |method| up = send(method, output) } up || 0 end def uptime_file '/proc/uptime' end def uptime_sysctl_variable 'kern.boottime' end def uptime_executable_cmd 'uptime' end def output_calculator_methods %i[ calculate_days_hours_minutes calculate_days_hours calculate_days_minutes calculate_days calculate_hours_minutes calculate_hours calculate_minutes ] end def compute_uptime(time) (Time.now - time).to_i end # Regexp handles Solaris, AIX, HP-UX, and Tru64. # 'day(?:s|\(s\))?' says maybe 'day', 'days', # or 'day(s)', and don't set $2. def calculate_days_hours_minutes(output) return unless output =~ /(\d+) day(?:s|\(s\))?,?\s+(\d+):-?(\d+)/ SECS_IN_A_DAY * Regexp.last_match(1).to_i + SECS_IN_AN_HOUR * Regexp.last_match(2).to_i + SECS_IN_A_MINUTE * Regexp.last_match(3).to_i end def calculate_days_hours(output) return unless output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) hr(?:s|\(s\))?,/ SECS_IN_A_DAY * Regexp.last_match(1).to_i + SECS_IN_AN_HOUR * Regexp.last_match(2).to_i end def calculate_days_minutes(output) return unless output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) min(?:s|\(s\))?,/ SECS_IN_A_DAY * Regexp.last_match(1).to_i + SECS_IN_A_MINUTE * Regexp.last_match(2).to_i end def calculate_days(output) return unless output =~ /(\d+) day(?:s|\(s\))?,/ SECS_IN_A_DAY * Regexp.last_match(1).to_i end # must anchor to 'up' to avoid matching time of day # at beginning of line. Certain versions of uptime on # Solaris may insert a '-' into the minutes field. def calculate_hours_minutes(output) return unless output =~ /up\s+(\d+):-?(\d+),/ SECS_IN_AN_HOUR * Regexp.last_match(1).to_i + SECS_IN_A_MINUTE * Regexp.last_match(2).to_i end def calculate_hours(output) return unless output =~ /(\d+) hr(?:s|\(s\))?,/ SECS_IN_AN_HOUR * Regexp.last_match(1).to_i end def calculate_minutes(output) return unless output =~ /(\d+) min(?:s|\(s\))?,/ SECS_IN_A_MINUTE * Regexp.last_match(1).to_i end end end end