188 lines
8.0 KiB
Python
188 lines
8.0 KiB
Python
# We can manually bootstrap and alter pacman's keyring. But check the bootstrap tarball; we might not need to.
|
|
# TODO.
|
|
|
|
import configparser
|
|
import logging
|
|
import os
|
|
import re
|
|
##
|
|
import pyalpm
|
|
import gpg
|
|
from lxml import etree
|
|
##
|
|
from . import _common
|
|
from . import keyring
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
# TODO: There is some duplication here that we can get rid of in the future. Namely:
|
|
# - Mirror URI parsing
|
|
# - Unified function for parsing Includes
|
|
# - At some point, ideally there should be a MirrorList class that can take (or generate?) a list of Mirrors
|
|
# and have a write function to write out a mirror list to a specified location.
|
|
|
|
|
|
class Mirror(object):
|
|
def __init__(self, mirror_xml, repo = None, arch = None):
|
|
self.xml = mirror_xml
|
|
_logger.debug('mirror_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
|
self.uri = self.xml.text
|
|
self.real_uri = None
|
|
self.aif_uri = None
|
|
|
|
def parse(self, chroot_base, repo, arch):
|
|
self.real_uri = self.uri.replace('$repo', repo).replace('$arch', arch)
|
|
if self.uri.startswith('file://'):
|
|
self.aif_uri = os.path.join(chroot_base, re.sub(r'^file:///?', ''))
|
|
|
|
|
|
class Package(object):
|
|
def __init__(self, package_xml):
|
|
self.xml = package_xml
|
|
_logger.debug('package_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
|
self.name = self.xml.text
|
|
self.repo = self.xml.attrib.get('repo')
|
|
if self.repo:
|
|
self.qualified_name = '{0}/{1}'.format(self.repo, self.name)
|
|
else:
|
|
self.qualified_name = self.name
|
|
|
|
|
|
class PackageManager(object):
|
|
def __init__(self, chroot_base, pacman_xml):
|
|
self.xml = pacman_xml
|
|
_logger.debug('pacman_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
|
self.chroot_base = chroot_base
|
|
self.pacman_dir = os.path.join(self.chroot_base, 'var', 'lib', 'pacman')
|
|
self.configfile = os.path.join(self.chroot_base, 'etc', 'pacman.conf')
|
|
self.keyring = keyring.PacmanKey(self.chroot_base)
|
|
self.config = None
|
|
self.handler = None
|
|
self.repos = []
|
|
self.packages = []
|
|
self.mirrorlist = []
|
|
self._initHandler()
|
|
self._initMirrors()
|
|
self._initRepos()
|
|
|
|
def _initHandler(self):
|
|
# TODO: Append mirrors/repos to pacman.conf here before we parse?
|
|
self.opts = {'Architecture': 'x86_64', # Technically, "auto" but Arch proper only supports x86_64.
|
|
'CacheDir': '/var/cache/pacman/pkg/',
|
|
'CheckSpace': True,
|
|
'CleanMethod': 'KeepInstalled',
|
|
# 'Color': None,
|
|
'DBPath': '/var/lib/pacman/',
|
|
'GPGDir': '/etc/pacman.d/gnupg/',
|
|
'HoldPkg': 'pacman glibc',
|
|
'HookDir': '/etc/pacman.d/hooks/',
|
|
'IgnoreGroup': '',
|
|
'IgnorePkg': '',
|
|
'LocalFileSigLevel': 'Optional',
|
|
'LogFile': '/var/log/pacman.log',
|
|
'NoExtract': '',
|
|
'NoUpgrade': '',
|
|
'RemoteFileSigLevel': 'Required',
|
|
'RootDir': '/',
|
|
'SigLevel': 'Required DatabaseOptional',
|
|
# 'TotalDownload': None,
|
|
# 'UseSyslog': None,
|
|
# 'VerbosePkgLists': None,
|
|
'XferCommand': '/usr/bin/curl -L -C - -f -o %o %u'
|
|
}
|
|
for k, v in self.opts.items():
|
|
if k in ('CacheDir', 'DBPath', 'GPGDir', 'HookDir', 'LogFile', 'RootDir'):
|
|
v = re.sub(r'^/+', r'', v)
|
|
self.opts[k] = os.path.join(self.chroot_base, v)
|
|
if k in ('HoldPkg', 'IgnoreGroup', 'IgnorePkg', 'NoExtract', 'NoUpgrade', 'SigLevel'):
|
|
v = v.split()
|
|
if not self.handler:
|
|
self.handler = pyalpm.Handle(self.chroot_base, self.pacman_dir)
|
|
# Pretty much blatantly ripped this off of pycman:
|
|
# https://github.com/archlinux/pyalpm/blob/master/pycman/config.py
|
|
for k in ('LogFile', 'GPGDir', 'NoExtract', 'NoUpgrade'):
|
|
setattr(self.handler, k.lower(), self.opts[k])
|
|
self.handler.arch = self.opts['Architecture']
|
|
if self.opts['IgnoreGroup']:
|
|
self.handler.ignoregrps = self.opts['IgnoreGroup']
|
|
if self.opts['IgnorePkg']:
|
|
self.handler.ignorepkgs = self.opts['IgnorePkg']
|
|
return(None)
|
|
|
|
def _initMirrors(self):
|
|
mirrors = self.xml.find('mirrorList')
|
|
if mirrors:
|
|
_mirrorlist = os.path.join(self.chroot_base, 'etc', 'pacman.d', 'mirrorlist')
|
|
with open(_mirrorlist, 'a') as fh:
|
|
fh.write('\n# Added by AIF-NG.\n')
|
|
for m in mirrors.findall('mirror'):
|
|
mirror = Mirror(m)
|
|
self.mirrorlist.append(mirror)
|
|
fh.write('Server = {0}\n'.format(mirror.uri))
|
|
_logger.info('Appended: {0}'.format(_mirrorlist))
|
|
return(None)
|
|
|
|
def _initRepos(self):
|
|
repos = self.xml.find('repos')
|
|
_conf = os.path.join(self.chroot_base, 'etc', 'pacman.conf')
|
|
with open(_conf, 'a') as fh:
|
|
fh.write('\n# Added by AIF-NG.\n')
|
|
for r in repos.findall('repo'):
|
|
repo = Repo(self.chroot_base, r)
|
|
if repo.enabled:
|
|
fh.write('[{0}]\n'.format(repo.name))
|
|
if repo.siglevel:
|
|
fh.write('SigLevel = {0}\n'.format(repo.siglevel))
|
|
if repo.uri:
|
|
fh.write('Server = {0}\n'.format(repo.uri))
|
|
else:
|
|
fh.write('Include = /etc/pacman.d/mirrorlist\n')
|
|
else:
|
|
fh.write('#[{0}]\n'.format(repo.name))
|
|
if repo.siglevel:
|
|
fh.write('#SigLevel = {0}\n'.format(repo.siglevel))
|
|
if repo.uri:
|
|
fh.write('#Server = {0}\n'.format(repo.uri))
|
|
else:
|
|
fh.write('#Include = /etc/pacman.d/mirrorlist\n')
|
|
self.repos.append(repo)
|
|
_logger.info('Appended: {0}'.format(_conf))
|
|
return(None)
|
|
|
|
|
|
class Repo(object):
|
|
def __init__(self, chroot_base, repo_xml, arch = 'x86_64'):
|
|
# TODO: support Usage? ("REPOSITORY SECTIONS", pacman.conf(5))
|
|
self.xml = repo_xml
|
|
_logger.debug('repo_xml: {0}'.format(etree.tostring(self.xml, with_tail = False).decode('utf-8')))
|
|
# TODO: SigLevels?!
|
|
self.name = self.xml.attrib['name']
|
|
self.mirrors = {}
|
|
self.parsed_mirrors = []
|
|
_mirrors = self.xml.xpath('mirror|include') # "Server" and "Include" respectively in pyalpm lingo.
|
|
if _mirrors:
|
|
for m in _mirrors:
|
|
k = m.tag.title()
|
|
if k == 'Mirror':
|
|
k = 'Server'
|
|
if k not in self.mirrors.keys():
|
|
self.mirrors[k] = []
|
|
self.mirrors[k].append(m.text)
|
|
if m.tag == 'include':
|
|
file_uri = os.path.join(chroot_base, re.sub(r'^file:///?', '', m.text))
|
|
if not os.path.isfile(file_uri):
|
|
_logger.error('Include file ({0}) does not exist: {1}'.format(m.text, file_uri))
|
|
raise FileNotFoundError('Include file does not exist')
|
|
with open(file_uri, 'r') as fh:
|
|
for line in fh.read().splitlines():
|
|
else:
|
|
# Default (mirrorlist)
|
|
self.mirrors['Include'] = ['file:///etc/pacman.d/mirrorlist']
|
|
self.enabled = (True if self.xml.attrib.get('enabled', 'true') in ('1', 'true') else False)
|
|
self.siglevel = self.xml.attrib.get('sigLevel')
|
|
self.real_uri = None
|
|
if self.uri:
|
|
self.real_uri = self.uri.replace('$repo', self.name).replace('$arch', arch)
|