#!/usr/bin/env python3 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

# # NOTE: this actually needs to run under python2.4 and centos 5.x through python3 and centos 7.x # please manually test changes on centos5 boxes or you will almost certainly break things. #

import sys import yum import signal import os sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'simplejson')) try: import json except ImportError: import simplejson as json import re from rpmUtils.miscutils import stringToVersion,compareEVR from rpmUtils.arch import getBaseArch, getArchList

try: from yum.misc import string_to_prco_tuple except ImportError:

# RHEL5 compat
def string_to_prco_tuple(prcoString):
    prco_split = prcoString.split()
    n, f, v = prco_split
    (prco_e, prco_v, prco_r) = stringToVersion(v)
    return (n, f, (prco_e, prco_v, prco_r))

base = None

def get_base():

global base
if base is None:
    base = yum.YumBase()
setup_exit_handler()
return base

def versioncompare(versions):

arch_list = getArchList()
candidate_arch1 = versions[0].split(".")[-1]
candidate_arch2 = versions[1].split(".")[-1]

# The first version number passed to this method is always a valid nevra (the current version)
# If the second version number looks like it does not contain a valid arch
# then we'll chop the arch component (assuming it *is* a valid one) from the first version string
# so we're only comparing the evr portions.
if (candidate_arch2 not in arch_list) and (candidate_arch1 in arch_list):
   final_version1 = versions[0].replace("." + candidate_arch1,"")
else:
   final_version1 = versions[0]

final_version2 = versions[1]

(e1, v1, r1) = stringToVersion(final_version1)
(e2, v2, r2) = stringToVersion(final_version2)

evr_comparison = compareEVR((e1, v1, r1), (e2, v2, r2))
outpipe.write("%(e)s\n" % { 'e': evr_comparison })
outpipe.flush()

def install_only_packages(name):

base = get_base()
if name in base.conf.installonlypkgs:
  outpipe.write('True')
else:
  outpipe.write('False')
outpipe.flush()

# python2.4 / centos5 compat try:

any

except NameError:

def any(s):
    for v in s:
        if v:
            return True
    return False

def query(command):

base = get_base()

enabled_repos = base.repos.listEnabled()

# Handle any repocontrols passed in with our options

if 'repos' in command:
  for repo in command['repos']:
    if 'enable' in repo:
      base.repos.enableRepo(repo['enable'])
    if 'disable' in repo:
      base.repos.disableRepo(repo['disable'])

args = { 'name': command['provides'] }
do_nevra = False
if 'epoch' in command:
    args['epoch'] = command['epoch']
    do_nevra = True
if 'version' in command:
    args['ver'] = command['version']
    do_nevra = True
if 'release' in command:
    args['rel'] = command['release']
    do_nevra = True
if 'arch' in command:
    desired_arch = command['arch']
    args['arch'] = command['arch']
    do_nevra = True
else:
    desired_arch = getBaseArch()

obj = None
if command['action'] == "whatinstalled":
    obj = base.rpmdb
else:
    obj = base.pkgSack

# if we are given "name == 1.2.3" then we must use the getProvides() API.
#   - this means that we ignore arch and version properties when given prco tuples as a package_name
#   - in order to fix this, something would have to happen where getProvides was called first and
#     then the result was searchNevra'd.  please be extremely careful if attempting to fix that
#     since searchNevra does not support prco tuples.
if any(elem in command['provides'] for elem in r"<=>"):
    # handles flags (<, >, =, etc) and versions, but no wildcareds 
    pkgs = obj.getProvides(*string_to_prco_tuple(command['provides']))
elif do_nevra:
    # now if we're given version or arch properties explicitly, then we do a SearchNevra.
    #  - this means that wildcard version in the package_name with an arch property will not work correctly
    #  - again don't try to fix this just by pushing bugs around in the code, you would need to call
    #    returnPackages and searchProvides and then apply the Nevra filters to those results.
    pkgs = obj.searchNevra(**args)
    if (command['action'] == "whatinstalled") and (not pkgs):
      pkgs = obj.searchNevra(name=args['name'], arch=desired_arch)
else:
    pats = [command['provides']]
    pkgs = obj.returnPackages(patterns=pats)

    if not pkgs:
        # handles wildcards
        pkgs = obj.searchProvides(command['provides'])

if not pkgs:
    outpipe.write(command['provides'].split().pop(0)+' nil nil\n')
    outpipe.flush()
else:
    # make sure we picked the package with the highest version
    pkgs = base.bestPackagesFromList(pkgs,single_name=True)
    pkg = pkgs.pop(0)
    outpipe.write("%(n)s %(e)s:%(v)s-%(r)s %(a)s\n" % { 'n': pkg.name, 'e': pkg.epoch, 'v': pkg.version, 'r': pkg.release, 'a': pkg.arch })
    outpipe.flush()

# Reset any repos we were passed in enablerepo/disablerepo to the original state in enabled_repos
if 'repos' in command:
  for repo in command['repos']:
    if 'enable' in repo:
      if base.repos.getRepo(repo['enable']) not in enabled_repos:
        base.repos.disableRepo(repo['enable'])
    if 'disable' in repo:
      if base.repos.getRepo(repo['disable']) in enabled_repos:
        base.repos.enableRepo(repo['disable'])

# the design of this helper is that it should try to be 'brittle' and fail hard and exit in order # to keep process tables clean. additional error handling should probably be added to the retry loop # on the ruby side. def exit_handler(signal, frame):

base.closeRpmDB()
sys.exit(0)

def setup_exit_handler():

signal.signal(signal.SIGINT, exit_handler)
signal.signal(signal.SIGHUP, exit_handler)
signal.signal(signal.SIGPIPE, exit_handler)
signal.signal(signal.SIGQUIT, exit_handler)

if len(sys.argv) < 3:

inpipe = sys.stdin
outpipe = sys.stdout

else:

inpipe = os.fdopen(int(sys.argv[1]), "r")
outpipe = os.fdopen(int(sys.argv[2]), "w")

while 1:

# kill self if we get orphaned (tragic)
ppid = os.getppid()
if ppid == 1:
    sys.exit(0)
setup_exit_handler()
line = inpipe.readline()

try:
    command = json.loads(line)
except ValueError, e:
    base.closeRpmDB()
    sys.exit(0)

if command['action'] == "whatinstalled":
    query(command)
elif command['action'] == "whatavailable":
    query(command)
elif command['action'] == "versioncompare":
    versioncompare(command['versions'])
elif command['action'] == "installonlypkgs":
     install_only_packages(command['package'])
else:
    raise RuntimeError("bad command")