#!/usr/bin/ruby
#
# Check for pending security updates, via `apt-get`.
#
# Thsi check is only used when `unattended-upgrades` is not available, as
# `apt_upgrade_check` is preferred.
#
#

require 'yaml'


#
#  This class either a) returns the output of running commands, or
# b) returns the output from a faked command
#
class CommandWrapper

  def CommandWrapper.run_command( str )

    if ( ENV['TEST'] && ENV['TEST_PREFIX'] )
      #
      #  Count the number of commands we've executed so far.
      #
      count = ENV['TEST_COUNT'] || '0'
      count = count.to_i
      ENV['TEST_COUNT'] = (count + 1).to_s

      #
      #  Read the output from the faked file.
      #
      #    $prefix/$count.cmd
      #
      file = "#{ENV['TEST_PREFIX']}/#{count}.cmd"
      File.open( file, "r" ).readlines().join()
    else
      `#{str}`
    end
  end
end


#
# Test to see if we have pending package upgrades.
#
# We only alert upon security upgrades.
#
class AptUpgrade

  #
  #  Is this a debian system?
  #
  def is_debian?
    File.exist?("/usr/bin/apt-get")  &&
      File.directory?("/var/lib/apt/lists")
  end


  #
  #  Return a hash of upgradable Debian packages.
  #
  #  The key contains the package-name, and the value contains the
  # origin of the upgrade.
  #
  def upgradable_packages
    ret = Hash.new

    out = CommandWrapper.run_command( "apt-get upgrade --simulate" )
    out.split( "\n" ).each do |line|

      # Split the line which will look something like:
      #
      #   Inst tacacs+-sso [1.1] (1.1 Unknown:maker2.bytemark.co.uk [amd64])
      #   Inst postgresql-contrib-9.3 [9.3.9-1.pgdg70+1] (9.3.10-1.pgdg70+1 PostgreSQL for Debian/Ubuntu repository:wheezy-pgdg [amd64]) []
      #
      if ( line =~ /^Inst\s([^\s+]+)\s.*\(([^)]+)\)/ )
        package = $1.dup
        details = $2.dup


        #
        #  The details contains:
        #
        #   version some-long-string [arch]
        #
        #  For example:
        #
        #    0.11.0-1+deb7u2 Debian-Security:7.0/oldstable [amd64]
        #
        if ( details =~ /^([^\s]+)\s+([^\[]+)\[(.*)$/ )

          #
          # This is the "origin" of the upgrade.
          #
          details = $2.dup
        end

        #
        #  Save the details away to our caller.
        #
        ret[package]=details
      end
    end

    ret
  end

  #
  #  Calculate upgrades which are ONLY security upgrades
  #
  def security_upgrades
    all = upgradable_packages
    ret = {}

    all.each do |key,value|
      ret[key] = value if ( value =~ /security/i )
    end

    ret
  end

end



if __FILE__ ==  $PROGRAM_NAME

  def verbose(str)
    STDERR.puts(str)
  end

  to_raise = []

  #
  # See how many pending upgrades are present.
  #
  helper   = AptUpgrade.new()
  upgrades = helper.is_debian? ? helper.security_upgrades : nil

  #
  # If unattend-upgrades is installed then we're doing nothing here.
  #
  if ( File.exists?( "/usr/bin/unattended-upgrades" ) )
    verbose( "Disabling test - /usr/bin/unattended-upgrades is present")
    upgrades = nil
  end

  if ( ! upgrades.nil? && !upgrades.empty? )
    h = {}
    h[:id] = "apt-get-upgrade-low"
    h[:summary] = "There are #{upgrades.size} pending security-upgrades available."
    h[:detail] = "<p>The following packages are pending security upgrades:</p><pre>#{upgrades.keys.join( "\n" )}</pre>"

    to_raise.push(h)
  end

  #
  #  Show the output.
  #
  puts YAML.dump(to_raise)
  exit(0)
end
