Back to index

apport  2.3
Public Member Functions | Public Attributes | Private Member Functions | Private Attributes
packaging-apt-dpkg.__AptDpkgPackageInfo Class Reference
Inheritance diagram for packaging-apt-dpkg.__AptDpkgPackageInfo:
Inheritance graph
[legend]
Collaboration diagram for packaging-apt-dpkg.__AptDpkgPackageInfo:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def __init__
def __del__
def get_version
def get_available_version
def get_dependencies
def get_source
def get_package_origin
def is_distro_package
def get_architecture
def get_files
def get_modified_files
def get_modified_conffiles
def get_file_package
def get_system_architecture
def get_library_paths
def set_mirror
def get_source_tree
def get_kernel_package
def install_packages
def package_name_glob
def compare_versions
def enabled
def get_source_tree
def get_uninstalled_package

Public Attributes

 configuration

Private Member Functions

def _virtual_mapping
def _save_virtual_mapping
def _cache
def _sandbox_cache
def _apt_pkg
def __fgrep_files
def _install_debug_kernel
def _call_dpkg
def _check_files_md5
def _get_mirror
def _search_contents
def _build_apt_sandbox
def _deb_version

Private Attributes

 _apt_cache
 _sandbox_apt_cache
 _contents_dir
 _mirror
 _virtual_mapping_obj

Detailed Description

Concrete apport.PackageInfo class implementation for python-apt and
dpkg, as found on Debian and derivatives such as Ubuntu.

Definition at line 34 of file packaging-apt-dpkg.py.


Constructor & Destructor Documentation

def packaging-apt-dpkg.__AptDpkgPackageInfo.__init__ (   self)

Definition at line 38 of file packaging-apt-dpkg.py.

00038 
00039     def __init__(self):
00040         self._apt_cache = None
00041         self._sandbox_apt_cache = None
00042         self._contents_dir = None
00043         self._mirror = None
00044         self._virtual_mapping_obj = None
00045 
00046         self.configuration = '/etc/default/apport'

def packaging-apt-dpkg.__AptDpkgPackageInfo.__del__ (   self)

Definition at line 47 of file packaging-apt-dpkg.py.

00047 
00048     def __del__(self):
00049         try:
00050             if self._contents_dir:
00051                 shutil.rmtree(self._contents_dir)
00052         except AttributeError:
00053             pass


Member Function Documentation

def packaging-apt-dpkg.__AptDpkgPackageInfo.__fgrep_files (   self,
  pattern,
  file_list 
) [private]
Call fgrep for a pattern on given file list and return the first
matching file, or None if no file matches.

Definition at line 304 of file packaging-apt-dpkg.py.

00304 
00305     def __fgrep_files(self, pattern, file_list):
00306         '''Call fgrep for a pattern on given file list and return the first
00307         matching file, or None if no file matches.'''
00308 
00309         match = None
00310         slice_size = 100
00311         i = 0
00312 
00313         while not match and i < len(file_list):
00314             p = subprocess.Popen(['fgrep', '-lxm', '1', '--', pattern] +
00315                                  file_list[i:(i + slice_size)], stdin=subprocess.PIPE,
00316                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00317             out = p.communicate()[0].decode('UTF-8')
00318             if p.returncode == 0:
00319                 match = out
00320             i += slice_size
00321 
00322         return match

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._apt_pkg (   self,
  package 
) [private]
Return apt.Cache()[package] (initialized lazily).

Throw a ValueError if the package does not exist.

Definition at line 107 of file packaging-apt-dpkg.py.

00107 
00108     def _apt_pkg(self, package):
00109         '''Return apt.Cache()[package] (initialized lazily).
00110 
00111         Throw a ValueError if the package does not exist.
00112         '''
00113         try:
00114             return self._cache()[package]
00115         except KeyError:
00116             raise ValueError('package does not exist')

Here is the call graph for this function:

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._build_apt_sandbox (   klass,
  apt_root,
  apt_sources 
) [private]

Definition at line 805 of file packaging-apt-dpkg.py.

00805 
00806     def _build_apt_sandbox(klass, apt_root, apt_sources):
00807         # pre-create directories, to avoid apt.Cache() printing "creating..."
00808         # messages on stdout
00809         if not os.path.exists(os.path.join(apt_root, 'var', 'lib', 'apt')):
00810             os.makedirs(os.path.join(apt_root, 'var', 'lib', 'apt', 'lists', 'partial'))
00811             os.makedirs(os.path.join(apt_root, 'var', 'cache', 'apt', 'archives', 'partial'))
00812             os.makedirs(os.path.join(apt_root, 'var', 'lib', 'dpkg'))
00813             os.makedirs(os.path.join(apt_root, 'etc', 'apt', 'apt.conf.d'))
00814             os.makedirs(os.path.join(apt_root, 'etc', 'apt', 'preferences.d'))
00815 
00816         # install apt sources
00817         list_d = os.path.join(apt_root, 'etc', 'apt', 'sources.list.d')
00818         if os.path.exists(list_d):
00819             shutil.rmtree(list_d)
00820         if os.path.isdir(apt_sources + '.d'):
00821             shutil.copytree(apt_sources + '.d', list_d)
00822         else:
00823             os.makedirs(list_d)
00824         with open(apt_sources) as src:
00825             with open(os.path.join(apt_root, 'etc', 'apt', 'sources.list'), 'w') as dest:
00826                 dest.write(src.read())
00827 
00828         # install apt keyrings; prefer the ones from the config dir, fall back
00829         # to system
00830         trusted_gpg = os.path.join(os.path.dirname(apt_sources), 'trusted.gpg')
00831         if os.path.exists(trusted_gpg):
00832             shutil.copy(trusted_gpg, os.path.join(apt_root, 'etc', 'apt'))
00833         elif os.path.exists('/etc/apt/trusted.gpg'):
00834             shutil.copy('/etc/apt/trusted.gpg', os.path.join(apt_root, 'etc', 'apt'))
00835 
00836         trusted_d = os.path.join(apt_root, 'etc', 'apt', 'trusted.gpg.d')
00837         if os.path.exists(trusted_d):
00838             shutil.rmtree(trusted_d)
00839 
00840         if os.path.exists(trusted_gpg + '.d'):
00841             shutil.copytree(trusted_gpg + '.d', trusted_d)
00842         elif os.path.exists('/etc/apt/trusted.gpg.d'):
00843             shutil.copytree('/etc/apt/trusted.gpg.d', trusted_d)
00844         else:
00845             os.makedirs(trusted_d)

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._cache (   self) [private]
Return apt.Cache() (initialized lazily).

Definition at line 73 of file packaging-apt-dpkg.py.

00073 
00074     def _cache(self):
00075         '''Return apt.Cache() (initialized lazily).'''
00076 
00077         self._sandbox_apt_cache = None
00078         if not self._apt_cache:
00079             try:
00080                 # avoid spewage on stdout
00081                 progress = apt.progress.base.OpProgress()
00082                 self._apt_cache = apt.Cache(progress, rootdir='/')
00083             except AttributeError:
00084                 # older python-apt versions do not yet have above argument
00085                 self._apt_cache = apt.Cache(rootdir='/')
00086         return self._apt_cache

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._call_dpkg (   klass,
  args 
) [private]
Call dpkg with given arguments and return output, or return None on
error.

Definition at line 691 of file packaging-apt-dpkg.py.

00691 
00692     def _call_dpkg(klass, args):
00693         '''Call dpkg with given arguments and return output, or return None on
00694         error.'''
00695 
00696         dpkg = subprocess.Popen(['dpkg'] + args, stdout=subprocess.PIPE,
00697                                 stderr=subprocess.PIPE)
00698         out = dpkg.communicate(input)[0].decode('UTF-8')
00699         if dpkg.returncode == 0:
00700             return out
00701         else:
00702             raise ValueError('package does not exist')

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._check_files_md5 (   self,
  sumfile 
) [private]
Internal function for calling md5sum.

This is separate from get_modified_files so that it is automatically
testable.

Definition at line 703 of file packaging-apt-dpkg.py.

00703 
00704     def _check_files_md5(self, sumfile):
00705         '''Internal function for calling md5sum.
00706 
00707         This is separate from get_modified_files so that it is automatically
00708         testable.
00709         '''
00710         if os.path.exists(sumfile):
00711             m = subprocess.Popen(['/usr/bin/md5sum', '-c', sumfile],
00712                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE,
00713                                  cwd='/', env={})
00714             out = m.communicate()[0].decode('UTF-8', errors='replace')
00715         else:
00716             assert type(sumfile) == bytes, 'md5sum list value must be a byte array'
00717             m = subprocess.Popen(['/usr/bin/md5sum', '-c'],
00718                                  stdin=subprocess.PIPE, stdout=subprocess.PIPE,
00719                                  stderr=subprocess.PIPE, cwd='/', env={})
00720             out = m.communicate(sumfile)[0].decode('UTF-8', errors='replace')
00721 
00722         # if md5sum succeeded, don't bother parsing the output
00723         if m.returncode == 0:
00724             return []
00725 
00726         mismatches = []
00727         for l in out.splitlines():
00728             if l.endswith('FAILED'):
00729                 mismatches.append(l.rsplit(':', 1)[0])
00730 
00731         return mismatches

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._deb_version (   klass,
  pkg 
) [private]
Return the version of a .deb file

Definition at line 847 of file packaging-apt-dpkg.py.

00847 
00848     def _deb_version(klass, pkg):
00849         '''Return the version of a .deb file'''
00850 
00851         dpkg = subprocess.Popen(['dpkg-deb', '-f', pkg, 'Version'], stdout=subprocess.PIPE)
00852         out = dpkg.communicate(input)[0].decode('UTF-8')
00853         assert dpkg.returncode == 0
00854         assert out
00855         return out

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._get_mirror (   self) [private]
Return the distribution mirror URL.

If it has not been set yet, it will be read from the system
configuration.

Definition at line 732 of file packaging-apt-dpkg.py.

00732 
00733     def _get_mirror(self):
00734         '''Return the distribution mirror URL.
00735 
00736         If it has not been set yet, it will be read from the system
00737         configuration.'''
00738 
00739         if not self._mirror:
00740             for l in open('/etc/apt/sources.list'):
00741                 fields = l.split()
00742                 if len(fields) >= 3 and fields[0] == 'deb' and fields[1].startswith('http://'):
00743                     self._mirror = fields[1]
00744                     break
00745             else:
00746                 raise SystemError('cannot determine default mirror: /etc/apt/sources.list does not contain a valid deb line')
00747 
00748         return self._mirror

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._install_debug_kernel (   self,
  report 
) [private]
Install kernel debug package

Ideally this would be just another package but the kernel is
special in various ways currently so we can not use the apt
method.

Definition at line 471 of file packaging-apt-dpkg.py.

00471 
00472     def _install_debug_kernel(self, report):
00473         '''Install kernel debug package
00474 
00475         Ideally this would be just another package but the kernel is
00476         special in various ways currently so we can not use the apt
00477         method.
00478         '''
00479         installed = []
00480         outdated = []
00481         kver = report['Uname'].split()[1]
00482         arch = report['Architecture']
00483         ver = report['Package'].split()[1]
00484         debug_pkgname = 'linux-image-debug-%s' % kver
00485         c = self._cache()
00486         if debug_pkgname in c and c[debug_pkgname].isInstalled:
00487             #print('kernel ddeb already installed')
00488             return (installed, outdated)
00489         target_dir = apt.apt_pkg.config.find_dir('Dir::Cache::archives') + '/partial'
00490         deb = '%s_%s_%s.ddeb' % (debug_pkgname, ver, arch)
00491         # FIXME: this package is currently not in Packages.gz
00492         url = 'http://ddebs.ubuntu.com/pool/main/l/linux/%s' % deb
00493         out = open(os.path.join(target_dir, deb), 'w')
00494         # urlretrieve does not return 404 in the headers so we use urlopen
00495         u = urlopen(url)
00496         if u.getcode() > 400:
00497             return ('', 'linux')
00498         while True:
00499             block = u.read(8 * 1024)
00500             if not block:
00501                 break
00502             out.write(block)
00503         out.flush()
00504         ret = subprocess.call(['dpkg', '-i', os.path.join(target_dir, deb)])
00505         if ret == 0:
00506             installed.append(deb.split('_')[0])
00507         return (installed, outdated)

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._sandbox_cache (   self,
  aptroot,
  apt_sources,
  fetchProgress 
) [private]
Build apt sandbox and return apt.Cache(rootdir=) (initialized lazily).

Clear the package selection on subsequent calls.

Definition at line 87 of file packaging-apt-dpkg.py.

00087 
00088     def _sandbox_cache(self, aptroot, apt_sources, fetchProgress):
00089         '''Build apt sandbox and return apt.Cache(rootdir=) (initialized lazily).
00090 
00091         Clear the package selection on subsequent calls.
00092         '''
00093         self._apt_cache = None
00094         if not self._sandbox_apt_cache:
00095             self._build_apt_sandbox(aptroot, apt_sources)
00096             rootdir = os.path.abspath(aptroot)
00097             self._sandbox_apt_cache = apt.Cache(rootdir=rootdir)
00098             try:
00099                 # We don't need to update this multiple times.
00100                 self._sandbox_apt_cache.update(fetchProgress)
00101             except apt.cache.FetchFailedException as e:
00102                 raise SystemError(str(e))
00103             self._sandbox_apt_cache.open()
00104         else:
00105             self._sandbox_apt_cache.clear()
00106         return self._sandbox_apt_cache

Here is the call graph for this function:

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._save_virtual_mapping (   self,
  configdir 
) [private]

Definition at line 67 of file packaging-apt-dpkg.py.

00067 
00068     def _save_virtual_mapping(self, configdir):
00069         mapping_file = os.path.join(configdir, 'virtual_mapping.pickle')
00070         if self._virtual_mapping_obj is not None:
00071             with open(mapping_file, 'wb') as fp:
00072                 pickle.dump(self._virtual_mapping_obj, fp)

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._search_contents (   self,
  file,
  map_cachedir 
) [private]
Internal function for searching file in Contents.gz.

Definition at line 749 of file packaging-apt-dpkg.py.

00749 
00750     def _search_contents(self, file, map_cachedir):
00751         '''Internal function for searching file in Contents.gz.'''
00752 
00753         if map_cachedir:
00754             dir = map_cachedir
00755         else:
00756             if not self._contents_dir:
00757                 self._contents_dir = tempfile.mkdtemp()
00758             dir = self._contents_dir
00759 
00760         arch = self.get_system_architecture()
00761         map = os.path.join(dir, 'Contents-%s.gz' % arch)
00762 
00763         # check if map exists and is younger than a day; if not, we need to
00764         # refresh it
00765         try:
00766             st = os.stat(map)
00767             age = int(time.time() - st.st_mtime)
00768         except OSError:
00769             age = None
00770 
00771         if age is None or age >= 86400:
00772             # determine distro release code name
00773             lsb_release = subprocess.Popen(['lsb_release', '-sc'],
00774                                            stdout=subprocess.PIPE)
00775             release_name = lsb_release.communicate()[0].decode('UTF-8').strip()
00776             assert lsb_release.returncode == 0
00777 
00778             url = '%s/dists/%s/Contents-%s.gz' % (self._get_mirror(), release_name, arch)
00779 
00780             src = urlopen(url)
00781             with open(map, 'wb') as f:
00782                 while True:
00783                     data = src.read(1000000)
00784                     if not data:
00785                         break
00786                     f.write(data)
00787             src.close()
00788             assert os.path.exists(map)
00789 
00790         if file.startswith('/'):
00791             file = file[1:]
00792 
00793         # zgrep is magnitudes faster than a 'gzip.open/split() loop'
00794         package = None
00795         zgrep = subprocess.Popen(['zgrep', '-m1', '^%s[[:space:]]' % file, map],
00796                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00797         out = zgrep.communicate()[0].decode('UTF-8')
00798         # we do not check the return code, since zgrep -m1 often errors out
00799         # with 'stdout: broken pipe'
00800         if out:
00801             package = out.split()[1].split(',')[0].split('/')[-1]
00802 
00803         return package

Here is the call graph for this function:

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo._virtual_mapping (   self,
  configdir 
) [private]

Definition at line 54 of file packaging-apt-dpkg.py.

00054 
00055     def _virtual_mapping(self, configdir):
00056         if self._virtual_mapping_obj is not None:
00057             return self._virtual_mapping_obj
00058 
00059         mapping_file = os.path.join(configdir, 'virtual_mapping.pickle')
00060         if os.path.exists(mapping_file):
00061             with open(mapping_file, 'rb') as fp:
00062                 self._virtual_mapping_obj = pickle.load(fp)
00063         else:
00064             self._virtual_mapping_obj = {}
00065 
00066         return self._virtual_mapping_obj

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.compare_versions (   self,
  ver1,
  ver2 
)
Compare two package versions.

Return -1 for ver < ver2, 0 for ver1 == ver2, and 1 for ver1 > ver2.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 856 of file packaging-apt-dpkg.py.

00856 
00857     def compare_versions(self, ver1, ver2):
00858         '''Compare two package versions.
00859 
00860         Return -1 for ver < ver2, 0 for ver1 == ver2, and 1 for ver1 > ver2.'''
00861 
00862         return apt.apt_pkg.version_compare(ver1, ver2)

def packaging-apt-dpkg.__AptDpkgPackageInfo.enabled (   self)
Return whether Apport should generate crash reports.

Signal crashes are controlled by /proc/sys/kernel/core_pattern, but
some init script needs to set that value based on a configuration file.
This also determines whether Apport generates reports for Python,
package, or kernel crashes.

Implementations should parse the configuration file which controls
Apport (such as /etc/default/apport in Debian/Ubuntu).

Reimplemented from apport.packaging.PackageInfo.

Definition at line 863 of file packaging-apt-dpkg.py.

00863 
00864     def enabled(self):
00865         '''Return whether Apport should generate crash reports.
00866 
00867         Signal crashes are controlled by /proc/sys/kernel/core_pattern, but
00868         some init script needs to set that value based on a configuration file.
00869         This also determines whether Apport generates reports for Python,
00870         package, or kernel crashes.
00871 
00872         Implementations should parse the configuration file which controls
00873         Apport (such as /etc/default/apport in Debian/Ubuntu).
00874         '''
00875 
00876         try:
00877             with open(self.configuration) as f:
00878                 conf = f.read()
00879         except IOError:
00880             # if the file does not exist, assume it's enabled
00881             return True
00882 
00883         return re.search('^\s*enabled\s*=\s*0\s*$', conf, re.M) is None

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_architecture (   self,
  package 
)
Return the architecture of a package.

This might differ on multiarch architectures (e. g.  an i386 Firefox
package on a x86_64 system)

Reimplemented from apport.packaging.PackageInfo.

Definition at line 198 of file packaging-apt-dpkg.py.

00198 
00199     def get_architecture(self, package):
00200         '''Return the architecture of a package.
00201 
00202         This might differ on multiarch architectures (e. g.  an i386 Firefox
00203         package on a x86_64 system)'''
00204 
00205         if self._apt_pkg(package).installed:
00206             return self._apt_pkg(package).installed.architecture or 'unknown'
00207         elif self._apt_pkg(package).candidate:
00208             return self._apt_pkg(package).candidate.architecture or 'unknown'
00209         else:
00210             raise ValueError('package %s does not exist' % package)

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_available_version (   self,
  package 
)
Return the latest available version of a package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 126 of file packaging-apt-dpkg.py.

00126 
00127     def get_available_version(self, package):
00128         '''Return the latest available version of a package.'''
00129 
00130         return self._apt_pkg(package).candidate.version

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_dependencies (   self,
  package 
)
Return a list of packages a package depends on.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 131 of file packaging-apt-dpkg.py.

00131 
00132     def get_dependencies(self, package):
00133         '''Return a list of packages a package depends on.'''
00134 
00135         cur_ver = self._apt_pkg(package)._pkg.current_ver
00136         if not cur_ver:
00137             # happens with virtual packages
00138             return []
00139         return [d[0].target_pkg.name for d in cur_ver.depends_list.get('Depends', []) +
00140                 cur_ver.depends_list.get('PreDepends', [])]

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_file_package (   self,
  file,
  uninstalled = False,
  map_cachedir = None 
)
Return the package a file belongs to, or None if the file is not
shipped by any package.

If uninstalled is True, this will also find files of uninstalled
packages; this is very expensive, though, and needs network access and
lots of CPU and I/O resources. In this case, map_cachedir can be set to
an existing directory which will be used to permanently store the
downloaded maps. If it is not set, a temporary directory will be used.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 323 of file packaging-apt-dpkg.py.

00323 
00324     def get_file_package(self, file, uninstalled=False, map_cachedir=None):
00325         '''Return the package a file belongs to, or None if the file is not
00326         shipped by any package.
00327 
00328         If uninstalled is True, this will also find files of uninstalled
00329         packages; this is very expensive, though, and needs network access and
00330         lots of CPU and I/O resources. In this case, map_cachedir can be set to
00331         an existing directory which will be used to permanently store the
00332         downloaded maps. If it is not set, a temporary directory will be used.
00333         '''
00334         # check if the file is a diversion
00335         dpkg = subprocess.Popen(['/usr/sbin/dpkg-divert', '--list', file],
00336                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00337         out = dpkg.communicate()[0].decode('UTF-8')
00338         if dpkg.returncode == 0 and out:
00339             pkg = out.split()[-1]
00340             if pkg != 'hardening-wrapper':
00341                 return pkg
00342 
00343         fname = os.path.splitext(os.path.basename(file))[0].lower()
00344 
00345         all_lists = []
00346         likely_lists = []
00347         for f in glob.glob('/var/lib/dpkg/info/*.list'):
00348             p = os.path.splitext(os.path.basename(f))[0].lower().split(':')[0]
00349             if p in fname or fname in p:
00350                 likely_lists.append(f)
00351             else:
00352                 all_lists.append(f)
00353 
00354         # first check the likely packages
00355         match = self.__fgrep_files(file, likely_lists)
00356         if not match:
00357             match = self.__fgrep_files(file, all_lists)
00358 
00359         if match:
00360             return os.path.splitext(os.path.basename(match))[0].split(':')[0]
00361 
00362         if uninstalled:
00363             return self._search_contents(file, map_cachedir)
00364         else:
00365             return None

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_files (   self,
  package 
)
Return list of files shipped by a package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 211 of file packaging-apt-dpkg.py.

00211 
00212     def get_files(self, package):
00213         '''Return list of files shipped by a package.'''
00214 
00215         list = self._call_dpkg(['-L', package])
00216         if list is None:
00217             return None
00218         return [f for f in list.splitlines() if not f.startswith('diverted')]

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_kernel_package (   self)
Return the actual Linux kernel package name.

This is used when the user reports a bug against the "linux" package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 463 of file packaging-apt-dpkg.py.

00463 
00464     def get_kernel_package(self):
00465         '''Return the actual Linux kernel package name.
00466 
00467         This is used when the user reports a bug against the "linux" package.
00468         '''
00469         # TODO: Ubuntu specific
00470         return 'linux-image-' + os.uname()[2]

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_library_paths (   self)
Return a list of default library search paths.

The entries should be separated with a colon ':', like for
$LD_LIBRARY_PATH. This needs to take any multiarch directories into
account.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 378 of file packaging-apt-dpkg.py.

00378 
00379     def get_library_paths(self):
00380         '''Return a list of default library search paths.
00381 
00382         The entries should be separated with a colon ':', like for
00383         $LD_LIBRARY_PATH. This needs to take any multiarch directories into
00384         account.
00385         '''
00386         dpkg = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'],
00387                                 stdout=subprocess.PIPE)
00388         multiarch_triple = dpkg.communicate()[0].decode().strip()
00389         assert dpkg.returncode == 0
00390 
00391         return '/lib/%s:/lib' % multiarch_triple

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_modified_conffiles (   self,
  package 
)
Return modified configuration files of a package.

Return a file name -> file contents map of all configuration files of
package. Please note that apport.hookutils.attach_conffiles() is the
official user-facing API for this, which will ask for confirmation and
allows filtering.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 267 of file packaging-apt-dpkg.py.

00267 
00268     def get_modified_conffiles(self, package):
00269         '''Return modified configuration files of a package.
00270 
00271         Return a file name -> file contents map of all configuration files of
00272         package. Please note that apport.hookutils.attach_conffiles() is the
00273         official user-facing API for this, which will ask for confirmation and
00274         allows filtering.
00275         '''
00276         dpkg = subprocess.Popen(['dpkg-query', '-W', '--showformat=${Conffiles}',
00277                                  package], stdout=subprocess.PIPE)
00278 
00279         out = dpkg.communicate()[0].decode()
00280         if dpkg.returncode != 0:
00281             return {}
00282 
00283         modified = {}
00284         for line in out.splitlines():
00285             if not line:
00286                 continue
00287             # just take the first two fields, to not stumble over obsolete
00288             # conffiles
00289             path, default_md5sum = line.strip().split()[:2]
00290 
00291             if os.path.exists(path):
00292                 with open(path, 'rb') as fd:
00293                     contents = fd.read()
00294                 m = hashlib.md5()
00295                 m.update(contents)
00296                 calculated_md5sum = m.hexdigest()
00297 
00298                 if calculated_md5sum != default_md5sum:
00299                     modified[path] = contents
00300             else:
00301                 modified[path] = '[deleted]'
00302 
00303         return modified

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_modified_files (   self,
  package 
)
Return list of all modified files of a package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 219 of file packaging-apt-dpkg.py.

00219 
00220     def get_modified_files(self, package):
00221         '''Return list of all modified files of a package.'''
00222 
00223         # get the maximum mtime of package files that we consider unmodified
00224         listfile = '/var/lib/dpkg/info/%s:%s.list' % (package, self.get_system_architecture())
00225         if not os.path.exists(listfile):
00226             listfile = '/var/lib/dpkg/info/%s.list' % package
00227         try:
00228             s = os.stat(listfile)
00229             if not stat.S_ISREG(s.st_mode):
00230                 raise OSError
00231             max_time = max(s.st_mtime, s.st_ctime)
00232         except OSError:
00233             return []
00234 
00235         # create a list of files with a newer timestamp for md5sum'ing
00236         sums = b''
00237         sumfile = '/var/lib/dpkg/info/%s:%s.md5sums' % (package, self.get_system_architecture())
00238         if not os.path.exists(sumfile):
00239             sumfile = '/var/lib/dpkg/info/%s.md5sums' % package
00240             if not os.path.exists(sumfile):
00241                 # some packages do not ship md5sums
00242                 return []
00243 
00244         with open(sumfile, 'rb') as fd:
00245             for line in fd:
00246                 try:
00247                     # ignore lines with NUL bytes (happens, LP#96050)
00248                     if b'\0' in line:
00249                         apport.warning('%s contains NUL character, ignoring line', sumfile)
00250                         continue
00251                     words = line.split()
00252                     if not words:
00253                         apport.warning('%s contains empty line, ignoring line', sumfile)
00254                         continue
00255                     s = os.stat('/' + words[-1].decode('UTF-8'))
00256                     if max(s.st_mtime, s.st_ctime) <= max_time:
00257                         continue
00258                 except OSError:
00259                     pass
00260 
00261                 sums += line
00262 
00263         if sums:
00264             return self._check_files_md5(sums)
00265         else:
00266             return []

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_package_origin (   self,
  package 
)
Return package origin.

Return the repository name from which a package was installed, or None
if it cannot be determined.

Throw ValueError if package is not installed.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 151 of file packaging-apt-dpkg.py.

00151 
00152     def get_package_origin(self, package):
00153         '''Return package origin.
00154 
00155         Return the repository name from which a package was installed, or None
00156         if it cannot be determined.
00157 
00158         Throw ValueError if package is not installed.
00159         '''
00160         pkg = self._apt_pkg(package).installed
00161         if not pkg:
00162             raise ValueError('package is not installed')
00163         for origin in pkg.origins:
00164             if origin.origin:
00165                 return origin.origin
00166         return None

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_source (   self,
  package 
)
Return the source package name for a package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 141 of file packaging-apt-dpkg.py.

00141 
00142     def get_source(self, package):
00143         '''Return the source package name for a package.'''
00144 
00145         if self._apt_pkg(package).installed:
00146             return self._apt_pkg(package).installed.source_name
00147         elif self._apt_pkg(package).candidate:
00148             return self._apt_pkg(package).candidate.source_name
00149         else:
00150             raise ValueError('package %s does not exist' % package)

Here is the call graph for this function:

def apport.packaging.PackageInfo.get_source_tree (   self,
  srcpackage,
  dir,
  version = None 
) [inherited]
Download a source package and unpack it into dir..

dir should exist and be empty.

This also has to care about applying patches etc., so that dir will
eventually contain the actually compiled source.

If version is given, this particular version will be retrieved.
Otherwise this will fetch the latest available version.

Return the directory that contains the actual source root directory
(which might be a subdirectory of dir). Return None if the source is
not available.

Definition at line 131 of file packaging.py.

00131 
00132     def get_source_tree(self, srcpackage, dir, version=None):
00133         '''Download a source package and unpack it into dir..
00134 
00135         dir should exist and be empty.
00136 
00137         This also has to care about applying patches etc., so that dir will
00138         eventually contain the actually compiled source.
00139 
00140         If version is given, this particular version will be retrieved.
00141         Otherwise this will fetch the latest available version.
00142 
00143         Return the directory that contains the actual source root directory
00144         (which might be a subdirectory of dir). Return None if the source is
00145         not available.
00146         '''
00147         raise NotImplementedError('this method must be implemented by a concrete subclass')

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_source_tree (   self,
  srcpackage,
  dir,
  version = None,
  sandbox = None,
  apt_update = False 
)
Download source package and unpack it into dir.

This also has to care about applying patches etc., so that dir will
eventually contain the actually compiled source. dir needs to exist and
should be empty.

If version is given, this particular version will be retrieved.
Otherwise this will fetch the latest available version.

If sandbox is given, it calls apt-get source in that sandbox, otherwise
it uses the system apt configuration.

If apt_update is True, it will call apt-get update before apt-get
source. This is mostly necessary for freshly created sandboxes.

Return the directory that contains the actual source root directory
(which might be a subdirectory of dir). Return None if the source is
not available.

Definition at line 402 of file packaging-apt-dpkg.py.

00402 
00403                         apt_update=False):
00404         '''Download source package and unpack it into dir.
00405 
00406         This also has to care about applying patches etc., so that dir will
00407         eventually contain the actually compiled source. dir needs to exist and
00408         should be empty.
00409 
00410         If version is given, this particular version will be retrieved.
00411         Otherwise this will fetch the latest available version.
00412 
00413         If sandbox is given, it calls apt-get source in that sandbox, otherwise
00414         it uses the system apt configuration.
00415 
00416         If apt_update is True, it will call apt-get update before apt-get
00417         source. This is mostly necessary for freshly created sandboxes.
00418 
00419         Return the directory that contains the actual source root directory
00420         (which might be a subdirectory of dir). Return None if the source is
00421         not available.
00422         '''
00423         # configure apt for sandbox
00424         env = os.environ.copy()
00425         if sandbox:
00426             f = tempfile.NamedTemporaryFile()
00427             f.write(('''Dir "%s";
00428 Debug::NoLocking "true";
00429  ''' % sandbox).encode())
00430             f.flush()
00431             env['APT_CONFIG'] = f.name
00432 
00433         if apt_update:
00434             subprocess.call(['apt-get', '-qq', 'update'], env=env)
00435 
00436         # fetch source tree
00437         argv = ['apt-get', '-qq', '--assume-yes', 'source', srcpackage]
00438         if version:
00439             argv[-1] += '=' + version
00440         try:
00441             if subprocess.call(argv, cwd=dir, env=env) != 0:
00442                 return None
00443         except OSError:
00444             return None
00445 
00446         # find top level directory
00447         root = None
00448         for d in glob.glob(os.path.join(dir, srcpackage + '-*')):
00449             if os.path.isdir(d):
00450                 root = d
00451         assert root, 'could not determine source tree root directory'
00452 
00453         # apply patches on a best-effort basis
00454         try:
00455             subprocess.call('(debian/rules patch || debian/rules apply-patches '
00456                             '|| debian/rules apply-dpatches || '
00457                             'debian/rules unpack || debian/rules patch-stamp || '
00458                             'debian/rules setup) >/dev/null 2>&1', shell=True, cwd=root)
00459         except OSError:
00460             pass
00461 
00462         return root

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_system_architecture (   klass)
Return the architecture of the system, in the notation used by the
particular distribution.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 367 of file packaging-apt-dpkg.py.

00367 
00368     def get_system_architecture(klass):
00369         '''Return the architecture of the system, in the notation used by the
00370         particular distribution.'''
00371 
00372         dpkg = subprocess.Popen(['dpkg', '--print-architecture'],
00373                                 stdout=subprocess.PIPE)
00374         arch = dpkg.communicate()[0].decode().strip()
00375         assert dpkg.returncode == 0
00376         assert arch
00377         return arch

Return a valid package name which is not installed.

This is only used in the test suite. The default implementation should
work, but might be slow for your backend, so you might want to
reimplement this.

Definition at line 215 of file packaging.py.

00215 
00216     def get_uninstalled_package(self):
00217         '''Return a valid package name which is not installed.
00218 
00219         This is only used in the test suite. The default implementation should
00220         work, but might be slow for your backend, so you might want to
00221         reimplement this.
00222         '''
00223         for p in self.package_name_glob('*'):
00224             if not self.is_distro_package(p):
00225                 continue
00226             try:
00227                 self.get_version(p)
00228                 continue
00229             except ValueError:
00230                 return p

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.get_version (   self,
  package 
)
Return the installed version of a package.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 117 of file packaging-apt-dpkg.py.

00117 
00118     def get_version(self, package):
00119         '''Return the installed version of a package.'''
00120 
00121         pkg = self._apt_pkg(package)
00122         inst = pkg.installed
00123         if not inst:
00124             raise ValueError('package does not exist')
00125         return inst.version

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.install_packages (   self,
  rootdir,
  configdir,
  release,
  packages,
  verbose = False,
  cache_dir = None,
  permanent_rootdir = False 
)
Install packages into a sandbox (for apport-retrace).

In order to work without any special permissions and without touching
the running system, this should only download and unpack packages into
the given root directory, not install them into the system.

configdir points to a directory with by-release configuration files for
the packaging system; this is completely dependent on the backend
implementation, the only assumption is that this looks into
configdir/release/, so that you can use retracing for multiple
DistroReleases. As a special case, if configdir is None, it uses the
current system configuration, and "release" is ignored.

release is the value of the report's 'DistroRelease' field.

packages is a list of ('packagename', 'version') tuples. If the version
is None, it should install the most current available version.

If cache_dir is given, then the downloaded packages will be stored
there, to speed up subsequent retraces.

If permanent_rootdir is True, then the sandbox created from the
downloaded packages will be reused, to speed up subsequent retraces.

Return a string with outdated packages, or None if all packages were
installed.

If something is wrong with the environment (invalid configuration,
package servers down, etc.), this should raise a SystemError with a
meaningful error message.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 509 of file packaging-apt-dpkg.py.

00509 
00510                          verbose=False, cache_dir=None, permanent_rootdir=False):
00511         '''Install packages into a sandbox (for apport-retrace).
00512 
00513         In order to work without any special permissions and without touching
00514         the running system, this should only download and unpack packages into
00515         the given root directory, not install them into the system.
00516 
00517         configdir points to a directory with by-release configuration files for
00518         the packaging system; this is completely dependent on the backend
00519         implementation, the only assumption is that this looks into
00520         configdir/release/, so that you can use retracing for multiple
00521         DistroReleases. As a special case, if configdir is None, it uses the
00522         current system configuration, and "release" is ignored.
00523 
00524         release is the value of the report's 'DistroRelease' field.
00525 
00526         packages is a list of ('packagename', 'version') tuples. If the version
00527         is None, it should install the most current available version.
00528 
00529         If cache_dir is given, then the downloaded packages will be stored
00530         there, to speed up subsequent retraces.
00531 
00532         If permanent_rootdir is True, then the sandbox created from the
00533         downloaded packages will be reused, to speed up subsequent retraces.
00534 
00535         Return a string with outdated packages, or None if all packages were
00536         installed.
00537 
00538         If something is wrong with the environment (invalid configuration,
00539         package servers down, etc.), this should raise a SystemError with a
00540         meaningful error message.
00541         '''
00542         if not configdir:
00543             apt_sources = '/etc/apt/sources.list'
00544         else:
00545             apt_sources = os.path.join(configdir, release, 'sources.list')
00546         if not os.path.exists(apt_sources):
00547             raise SystemError('%s does not exist' % apt_sources)
00548 
00549         # create apt sandbox
00550         if cache_dir:
00551             tmp_aptroot = False
00552             if configdir:
00553                 aptroot = os.path.join(cache_dir, release, 'apt')
00554             else:
00555                 aptroot = os.path.join(cache_dir, 'system', 'apt')
00556             if not os.path.isdir(aptroot):
00557                 os.makedirs(aptroot)
00558         else:
00559             tmp_aptroot = True
00560             aptroot = tempfile.mkdtemp()
00561 
00562         if verbose:
00563             fetchProgress = apt.progress.text.AcquireProgress()
00564         else:
00565             fetchProgress = apt.progress.base.AcquireProgress()
00566         if not tmp_aptroot:
00567             c = self._sandbox_cache(aptroot, apt_sources, fetchProgress)
00568         else:
00569             self._build_apt_sandbox(aptroot, apt_sources)
00570             c = apt.Cache(rootdir=os.path.abspath(aptroot))
00571             try:
00572                 c.update(fetchProgress)
00573             except apt.cache.FetchFailedException as e:
00574                 raise SystemError(str(e))
00575             c.open()
00576 
00577         obsolete = ''
00578 
00579         # mark packages for installation
00580         real_pkgs = set()
00581         for (pkg, ver) in packages:
00582             try:
00583                 candidate = c[pkg].candidate
00584             except KeyError:
00585                 candidate = None
00586             if not candidate:
00587                 m = 'package %s does not exist, ignoring' % pkg
00588                 obsolete += m + '\n'
00589                 apport.warning(m)
00590                 continue
00591 
00592             if ver and candidate.version != ver:
00593                 w = '%s version %s required, but %s is available' % (pkg, ver, candidate.version)
00594                 obsolete += w + '\n'
00595             real_pkgs.add(pkg)
00596 
00597             if permanent_rootdir:
00598                 mapping_path = os.path.join(cache_dir, release)
00599                 virtual_mapping = self._virtual_mapping(mapping_path)
00600                 # Remember all the virtual packages that this package provides,
00601                 # so that if we encounter that virtual package as a
00602                 # Conflicts/Replaces later, we know to remove this package from
00603                 # the cache.
00604                 for p in candidate.provides:
00605                     virtual_mapping.setdefault(p, set()).add(pkg)
00606                 conflicts = []
00607                 if 'Conflicts' in candidate.record:
00608                     conflicts += apt.apt_pkg.parse_depends(candidate.record['Conflicts'])
00609                 if 'Replaces' in candidate.record:
00610                     conflicts += apt.apt_pkg.parse_depends(candidate.record['Replaces'])
00611                 archives = apt.apt_pkg.config.find_dir('Dir::Cache::archives')
00612                 for conflict in conflicts:
00613                     # apt_pkg.parse_depends needs to handle the or operator,
00614                     # but as policy states it is invalid to use that in
00615                     # Replaces/Depends, we can safely choose the first value
00616                     # here.
00617                     conflict = conflict[0]
00618                     if c.is_virtual_package(conflict[0]):
00619                         try:
00620                             providers = virtual_mapping[conflict[0]]
00621                         except KeyError:
00622                             # We may not have seen the virtual package that
00623                             # this conflicts with, so we can assume it's not
00624                             # unpacked into the sandbox.
00625                             continue
00626                         for p in providers:
00627                             debs = os.path.join(archives, '%s_*.deb' % p)
00628                             for path in glob.glob(debs):
00629                                 ver = self._deb_version(path)
00630                                 if apt.apt_pkg.check_dep(ver, conflict[2],
00631                                                               conflict[1]):
00632                                     os.unlink(path)
00633                         del providers
00634                     else:
00635                         debs = os.path.join(archives, '%s_*.deb' % conflict[0])
00636                         for path in glob.glob(debs):
00637                             ver = self._deb_version(path)
00638                             if apt.apt_pkg.check_dep(ver, conflict[2],
00639                                                           conflict[1]):
00640                                 os.unlink(path)
00641 
00642             if candidate.architecture != 'all':
00643                 if pkg + '-dbg' in c:
00644                     real_pkgs.add(pkg + '-dbg')
00645                 elif pkg + '-dbgsym' in c:
00646                     real_pkgs.add(pkg + '-dbgsym')
00647                     if c[pkg + '-dbgsym'].candidate.version != candidate.version:
00648                         obsolete += 'outdated debug symbol package for %s: package version %s dbgsym version %s\n' % (
00649                             pkg, candidate.version, c[pkg + '-dbgsym'].candidate.version)
00650 
00651         for p in real_pkgs:
00652             c[p].mark_install(False, False)
00653 
00654         last_written = time.time()
00655         # fetch packages
00656         fetcher = apt.apt_pkg.Acquire(fetchProgress)
00657         try:
00658             c.fetch_archives(fetcher=fetcher)
00659         except apt.cache.FetchFailedException as e:
00660             apport.error('Package download error, try again later: %s', str(e))
00661             sys.exit(99)  # transient error
00662 
00663         # unpack packages
00664         if verbose:
00665             print('Extracting downloaded debs...')
00666         for i in fetcher.items:
00667             if not permanent_rootdir or os.path.getctime(i.destfile) > last_written:
00668                 subprocess.check_call(['dpkg', '-x', i.destfile, rootdir])
00669             real_pkgs.remove(os.path.basename(i.destfile).split('_', 1)[0])
00670 
00671         if tmp_aptroot:
00672             shutil.rmtree(aptroot)
00673 
00674         # check bookkeeping that apt fetcher really got everything
00675         assert not real_pkgs, 'apt fetcher did not fetch these packages: ' \
00676             + ' '.join(real_pkgs)
00677 
00678         if permanent_rootdir:
00679             self._save_virtual_mapping(mapping_path)
00680         return obsolete

Here is the call graph for this function:

Here is the caller graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.is_distro_package (   self,
  package 
)
Check if a package is a genuine distro package (True) or comes from
a third-party source.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 167 of file packaging-apt-dpkg.py.

00167 
00168     def is_distro_package(self, package):
00169         '''Check if a package is a genuine distro package (True) or comes from
00170         a third-party source.'''
00171 
00172         lsb_release = subprocess.Popen(['lsb_release', '-i', '-s'],
00173                                        stdout=subprocess.PIPE)
00174         this_os = lsb_release.communicate()[0].decode().strip()
00175         assert lsb_release.returncode == 0
00176 
00177         pkg = self._apt_pkg(package)
00178         # some PPA packages have installed version None, see LP#252734
00179         if pkg.installed and pkg.installed.version is None:
00180             return False
00181 
00182         native_origins = [this_os]
00183         for f in glob.glob('/etc/apport/native-origins.d/*'):
00184             try:
00185                 with open(f) as fd:
00186                     for line in fd:
00187                         line = line.strip()
00188                         if line:
00189                             native_origins.append(line)
00190             except IOError:
00191                 pass
00192 
00193         if pkg.candidate and pkg.candidate.origins:  # might be None
00194             for o in pkg.candidate.origins:
00195                 if o.origin in native_origins:
00196                     return True
00197         return False

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.package_name_glob (   self,
  nameglob 
)
Return known package names which match given glob.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 681 of file packaging-apt-dpkg.py.

00681 
00682     def package_name_glob(self, nameglob):
00683         '''Return known package names which match given glob.'''
00684 
00685         return glob.fnmatch.filter(self._cache().keys(), nameglob)

Here is the call graph for this function:

def packaging-apt-dpkg.__AptDpkgPackageInfo.set_mirror (   self,
  url 
)
Explicitly set a distribution mirror URL for operations that need to
fetch distribution files/packages from the network.

By default, the mirror will be read from the system configuration
files.

Reimplemented from apport.packaging.PackageInfo.

Definition at line 392 of file packaging-apt-dpkg.py.

00392 
00393     def set_mirror(self, url):
00394         '''Explicitly set a distribution mirror URL for operations that need to
00395         fetch distribution files/packages from the network.
00396 
00397         By default, the mirror will be read from the system configuration
00398         files.'''
00399 
00400         self._mirror = url

Here is the call graph for this function:


Member Data Documentation

packaging-apt-dpkg.__AptDpkgPackageInfo._apt_cache [private]

Definition at line 39 of file packaging-apt-dpkg.py.

packaging-apt-dpkg.__AptDpkgPackageInfo._contents_dir [private]

Definition at line 41 of file packaging-apt-dpkg.py.

packaging-apt-dpkg.__AptDpkgPackageInfo._mirror [private]

Definition at line 42 of file packaging-apt-dpkg.py.

packaging-apt-dpkg.__AptDpkgPackageInfo._sandbox_apt_cache [private]

Definition at line 40 of file packaging-apt-dpkg.py.

packaging-apt-dpkg.__AptDpkgPackageInfo._virtual_mapping_obj [private]

Definition at line 43 of file packaging-apt-dpkg.py.

packaging-apt-dpkg.__AptDpkgPackageInfo.configuration

Definition at line 45 of file packaging-apt-dpkg.py.


The documentation for this class was generated from the following file: