adequate-0.9.1ubuntu1/0000775000000000000000000000000012254266211011527 5ustar adequate-0.9.1ubuntu1/tests/0000775000000000000000000000000012254251542012672 5ustar adequate-0.9.1ubuntu1/tests/testpkg/0000775000000000000000000000000012254250210014342 5ustar adequate-0.9.1ubuntu1/tests/testpkg/test_pyc.py0000664000000000000000000000004612107472624016562 0ustar # This is a dummy Python source file. adequate-0.9.1ubuntu1/tests/testpkg/undef.c0000664000000000000000000000020512107517346015621 0ustar extern void this_symbol_might_be_undefined(void); int main(int argc, char **argv) { this_symbol_might_be_undefined(); return 0; } adequate-0.9.1ubuntu1/tests/testpkg/true0000775000000000000000000000002112253556054015256 0ustar #!/bin/sh exit 0 adequate-0.9.1ubuntu1/tests/testpkg/verscript-global0000664000000000000000000000004012107520111017533 0ustar ADEQUATE_TEST { global: *; }; adequate-0.9.1ubuntu1/tests/testpkg/debian/0000775000000000000000000000000012254254066015601 5ustar adequate-0.9.1ubuntu1/tests/testpkg/debian/rules0000775000000000000000000000627012254254066016666 0ustar #!/usr/bin/make -f tmp = debian/tmp at = adequate-test .PHONY: clean clean: dh_clean .PHONY: build build: build-stamp build-stamp: mkdir -p $(tmp) # bin-or-sbin-binary-requires-usr-lib-library; library-not-found $(CC) lib.c -fPIC -shared -Wl,-soname,libadequate-lib.so.0 -o $(tmp)/libadequate-lib.so.0 ln -sf libadequate-lib.so.0 $(tmp)/libadequate-lib.so $(CC) lib.c -fPIC -shared -Wl,-soname,libadequate-usrlib.so.0 -o $(tmp)/libadequate-usrlib.so.0 ln -sf libadequate-usrlib.so.0 $(tmp)/libadequate-usrlib.so $(CC) prog.c -L$(tmp) -Wl,--no-as-needed -ladequate-lib -o $(tmp)/adequate-lib1 cd $(tmp) && seq 2 5 | xargs -t -I {} ln -f adequate-lib1 adequate-lib{} $(CC) prog.c -L$(tmp) -Wl,--no-as-needed -ladequate-usrlib -o $(tmp)/adequate-usrlib1 cd $(tmp) && seq 2 5 | xargs -t -I {} ln -f adequate-usrlib1 adequate-usrlib{} # incompatible-licenses $(CC) lib.c -fPIC -shared -Wl,-soname,libssl.so.1.0.0 -o $(tmp)/libssl.so $(CC) lib.c -fPIC -shared -Wl,-soname,libreadline.so.6 -o $(tmp)/libreadline.so $(CC) prog.c -L$(tmp) -Wl,--no-as-needed -lssl -lreadline -o $(tmp)/adequate-license-conflict # missing-version-information $(CC) -shared -Wl,--soname=lib$(at)-versionless.so.0 -Wl,--version-script=verscript-global lib.c -o $(tmp)/lib$(at)-versionless.so.0 ln -sf lib$(at)-versionless.so.0 $(tmp)/lib$(at)-versionless.so $(CC) undef.c -L$(tmp) -o $(tmp)/$(at)-msvi -l$(at)-versionless $(CC) -shared -Wl,--soname=lib$(at)-versionless.so.0 lib.c -o $(tmp)/lib$(at)-versionless.so.0 # symbol-size-mismatch $(CC) -shared -Wl,--soname=lib$(at)-symsize.so.0 lib.c -o $(tmp)/lib$(at)-symsize.so.0 ln -sf lib$(at)-symsize.so.0 $(tmp)/lib$(at)-symsize.so $(CC) symsize.c -L$(tmp) -o $(tmp)/$(at)-symsize -l$(at)-symsize $(CC) -shared -Wl,--soname=lib$(at)-symsize.so.0 -DADEQUATE_SYMBOL_SIZE=42 lib.c -o $(tmp)/lib$(at)-symsize.so.0 # undefined-symbol $(CC) -shared -Wl,--soname=lib$(at)-versioning.so.0 -Wl,--version-script=verscript-global lib.c -o $(tmp)/lib$(at)-versioning.so.0 ln -sf lib$(at)-versioning.so.0 $(tmp)/lib$(at)-versioning.so $(CC) undef.c -fPIE -Wl,--unresolved-symbols,ignore-all -L$(tmp) -o $(tmp)/$(at)-us1 $(CC) undef.c -L$(tmp) -o $(tmp)/$(at)-us2 -l$(at)-versioning $(CC) -shared -Wl,--soname=lib$(at)-versioning.so.0 -Wl,--version-script=verscript-local lib.c -o $(tmp)/lib$(at)-versioning.so.0 # py-file-not-bytecompiled; pyshared-file-not-bytecompiled cp *.py debian/tmp/ python2.7 -m compileall debian/tmp/ touch $(@) .PHONY: build-arch build-indep build-arch build-indep: $(error $(@) is not implemented) .PHONY: binary binary: dh_install dh_installdocs -N adequate-testpkg-missing-copyright-file dh_installchangelogs dh_link dh_compress dh_fixperms dh_strip dh_makeshlibs dh_installdeb dh_gencontrol dh_md5sums dh_builddeb rm debian/adequate-testpkg-obsolete-conffile/etc/adequate/test-obsolete-conffile sed -i -e '/test-obsolete/d' debian/adequate-testpkg-obsolete-conffile/DEBIAN/conffiles dh_gencontrol -p adequate-testpkg-obsolete-conffile -- -v2 dh_md5sums -p adequate-testpkg-obsolete-conffile dh_builddeb -p adequate-testpkg-obsolete-conffile .PHONY: binary-arch binary-indep binary-arch binary-indep: $(error $(@) is not implemented) # vim:ts=4 sw=4 noet ././@LongLink0000644000000000000000000000016012254521445011643 Lustar rootrootadequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-bin-or-sbin-binary-requires-usr-lib-library.installadequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-bin-or-sbin-binary-requires-usr-lib-libr0000664000000000000000000000045012127353420030521 0ustar libadequate-lib.so.0 lib/ libadequate-usrlib.so.0 usr/lib/ adequate-lib1 bin/ adequate-lib2 sbin/ adequate-lib3 usr/bin/ adequate-lib4 usr/sbin/ adequate-lib5 usr/games/ adequate-usrlib1 bin/ adequate-usrlib2 sbin/ adequate-usrlib3 usr/bin/ adequate-usrlib4 usr/sbin/ adequate-usrlib5 usr/games/ adequate-0.9.1ubuntu1/tests/testpkg/debian/source/0000775000000000000000000000000012107475031017073 5ustar adequate-0.9.1ubuntu1/tests/testpkg/debian/source/format0000664000000000000000000000001412107474616020311 0ustar 3.0 (quilt) adequate-0.9.1ubuntu1/tests/testpkg/debian/changelog0000664000000000000000000000020512103200664017434 0ustar adequate-testpkg (1) unstable; urgency=low * Initial release. -- Jakub Wilk Sat, 02 Feb 2013 13:08:18 +0100 adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-undefined-symbol.install0000664000000000000000000000010512107521565025675 0ustar adequate-test-us* usr/bin/ libadequate-test-versioning.so.0 usr/lib/ adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-pyshared-file-not-bytecompiled.install0000664000000000000000000000006612103311060030426 0ustar test_pyshared_no_pyc.py /usr/share/pyshared/adequate/ adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-incompatible-licenses.install0000664000000000000000000000004312171024363026676 0ustar adequate-license-conflict /usr/bin adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-library-not-found.install0000664000000000000000000000002312127353422026000 0ustar adequate-lib1 bin/ adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-broken-symlink.links0000664000000000000000000000010112103042702025026 0ustar /this/file/does/not/exist usr/share/adequate/test-broken-symlink adequate-0.9.1ubuntu1/tests/testpkg/debian/copyright0000664000000000000000000000004012103312267017515 0ustar This is a dummy copyright file. adequate-0.9.1ubuntu1/tests/testpkg/debian/control0000664000000000000000000000612012254252023017172 0ustar Source: adequate-testpkg Section: misc Priority: extra Maintainer: Jakub Wilk Build-Depends: debhelper (>= 7), python2.7 Standards-Version: 3.9.4 Package: adequate-testpkg-bin-or-sbin-binary-requires-usr-lib-library Architecture: any Description: adequate test package; do not install bin-or-sbin-binary-requires-usr-lib-library /bin/adequate-usrlib1 => /usr/lib/libadequate-usrlib.so.0 bin-or-sbin-binary-requires-usr-lib-library /sbin/adequate-usrlib2 => /usr/lib/libadequate-usrlib.so.0 Package: adequate-testpkg-broken-symlink Architecture: all Description: adequate test package; do not install broken-symlink /usr/share/adequate/test-broken-symlink -> /this/file/does/not/exist Package: adequate-testpkg-library-not-found Architecture: any Description: adequate test package; do not install library-not-found /bin/adequate-lib1 => libadequate-lib.so.0 Package: adequate-testpkg-incompatible-licenses Architecture: any Description: adequate test package; do not install incompatible-licenses /usr/bin/adequate-license-conflict OpenSSL (libssl.so.1.0.0) + GPLv3+ (libreadline.so.6) Package: adequate-testpkg-missing-alternative Architecture: all Provides: x-terminal-emulator, x-window-manager Description: adequate test package; do not install missing-alternative x-terminal-emulator missing-alternative x-window-manager Package: adequate-testpkg-missing-copyright-file Architecture: all Description: adequate test package; do not install missing-copyright-file /usr/share/doc/adequate-testpkg-missing-copyright-file/copyright Package: adequate-testpkg-missing-symbol-version-information Architecture: any Description: adequate test package; do not install missing-symbol-version-information /usr/bin/adequate-test-msvi => /usr/lib/libadequate-test-versionless.so.0 Package: adequate-testpkg-obsolete-conffile Architecture: all Description: adequate test package; do not install obsolete-conffile /etc/adequate/test-obsolete-conffile Package: adequate-testpkg-program-name-collision Architecture: all Description: adequate test package; do not install program-name-collision /usr/games/true /bin/true Package: adequate-testpkg-py-file-not-bytecompiled Architecture: all Depends: python (>= 2.7), python (<< 2.8) Description: adequate test package; do not install py-file-not-bytecompiled /usr/lib/python2.7/dist-packages/adequate/test_no_pyc.py Package: adequate-testpkg-pyshared-file-not-bytecompiled Architecture: all Depends: python Description: adequate test package; do not install pyshared-file-not-bytecompiled /usr/share/pyshared/adequate/test_pyshared_no_pyc.py Package: adequate-testpkg-symbol-size-mismatch Architecture: any Description: adequate test package; do not install symbol-size-mismatch /usr/bin/adequate-test-symsize => this_symbol_size_varies Package: adequate-testpkg-undefined-symbol Architecture: any Description: adequate test package; do not install undefined-symbol /usr/bin/adequate-test-us1 => this_symbol_might_be_undefined undefined-symbol /usr/bin/adequate-test-us2 => this_symbol_might_be_undefined@ADEQUATE_TEST (libadequate-test-versioning.so.0) ././@LongLink0000644000000000000000000000014712254521445011650 Lustar rootrootadequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-missing-symbol-version-information.installadequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-missing-symbol-version-information.insta0000664000000000000000000000010712223535574031071 0ustar adequate-test-msvi usr/bin/ libadequate-test-versionless.so.0 usr/lib/ adequate-0.9.1ubuntu1/tests/testpkg/debian/compat0000664000000000000000000000000212103034734016766 0ustar 7 adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-py-file-not-bytecompiled.install0000664000000000000000000000043512107474023027254 0ustar test_no_pyc.py /usr/lib/python2.1/site-packages/adequate/ test_no_pyc.py /usr/lib/python2.7/dist-packages/adequate/ test_no_pyc.py /usr/lib/python2.9/dist-packages/adequate/ test_pyc.py /usr/lib/python2.7/dist-packages/adequate/ test_pyc.pyc /usr/lib/python2.7/dist-packages/adequate/ adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-obsolete-conffile.install0000664000000000000000000000012012103311103026002 0ustar test-non-obsolete-conffile /etc/adequate/ test-obsolete-conffile /etc/adequate/ adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-program-name-collision.install0000664000000000000000000000002012253556054027006 0ustar true /usr/games adequate-0.9.1ubuntu1/tests/testpkg/debian/adequate-testpkg-symbol-size-mismatch.install0000664000000000000000000000010612223535574026516 0ustar adequate-test-symsize usr/bin/ libadequate-test-symsize.so.0 usr/lib/ adequate-0.9.1ubuntu1/tests/testpkg/test-non-obsolete-conffile0000664000000000000000000000004612103312335021431 0ustar # This is a dummy configuration file. adequate-0.9.1ubuntu1/tests/testpkg/test_pyshared_no_pyc.py0000664000000000000000000000004612103312334021140 0ustar # This is a dummy Python source file. adequate-0.9.1ubuntu1/tests/testpkg/symsize.c0000664000000000000000000000016212223535574016227 0ustar extern char this_symbol_size_varies[37]; int main(int argc, char **argv) { return this_symbol_size_varies[0]; } adequate-0.9.1ubuntu1/tests/testpkg/test-obsolete-conffile0000664000000000000000000000004612103312346020643 0ustar # This is a dummy configuration file. adequate-0.9.1ubuntu1/tests/testpkg/verscript-local0000664000000000000000000000003712107517622017410 0ustar ADEQUATE_TEST { local: *; }; adequate-0.9.1ubuntu1/tests/testpkg/prog.c0000664000000000000000000000005712103051527015462 0ustar int main(int argc, char **argv) { return 0; } adequate-0.9.1ubuntu1/tests/testpkg/lib.c0000664000000000000000000000025512223535574015275 0ustar void this_symbol_might_be_undefined(void) { } #if !defined(ADEQUATE_SYMBOL_SIZE) #define ADEQUATE_SYMBOL_SIZE 37 #endif char this_symbol_size_varies[ADEQUATE_SYMBOL_SIZE]; adequate-0.9.1ubuntu1/tests/testpkg/test_no_pyc.py0000664000000000000000000000004612107472624017256 0ustar # This is a dummy Python source file. adequate-0.9.1ubuntu1/tests/Makefile0000664000000000000000000000154312254251513014333 0ustar arch = $(shell dpkg-architecture -qDEB_HOST_ARCH) changes = adequate-testpkg_1_$(arch).changes .PHONY: all all: $(changes) $(info Run "make run" as root.) $(info Alternatively, if you have user-mode-linux installed, you can try "make run-uml" as normal user.) $(info )@: $(changes): cd testpkg && dpkg-buildpackage -b -us -uc .PHONY: run run: $(changes) ./run-tests $(<) .PHONY: run-uml run-uml: $(changes) fallocate -l 1G uml-guest-swap setsid linux init=$(CURDIR)/uml-guest-init \ $(shell printf '%s' '$(CURDIR)' | base64 -w0 | tr = _) \ $(shell printf '%s' '$(<)' | base64 -w0 | tr = _) \ rootfstype=hostfs ubd0=uml-guest-swap mem=128M quiet con=null,fd:1 \ | sed '/: adequate tests succeeded :/ q 100'; \ [ $$? -eq 100 ] .PHONY: clean clean: cd testpkg && debian/rules clean rm -f *.changes *.deb rm -f uml-guest-swap # vim:ts=4 sw=4 noet adequate-0.9.1ubuntu1/tests/coverage.txt0000664000000000000000000000055412253556054015237 0ustar [x] bin-or-sbin-binary-requires-usr-lib-library [x] broken-symlink [x] incompatible-licenses [x] library-not-found [x] missing-alternative [x] missing-copyright-file [x] missing-symbol-version-information [x] obsolete-conffile [x] program-name-collision [x] py-file-not-bytecompiled [x] pyshared-file-not-bytecompiled [x] symbol-size-mismatch [x] undefined-symbol adequate-0.9.1ubuntu1/tests/run-tests0000775000000000000000000001344312253576043014576 0ustar #!/usr/bin/python3 # Copyright © 2013 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import argparse import collections import contextlib import difflib import functools import os import subprocess as ipc import sys import apt_pkg import apt.cache import apt.debfile @functools.total_ordering class debversion(str): def __lt__(self, other): return apt_pkg.version_compare(self, other) < 0 def __eq__(self, other): return apt_pkg.version_compare(self, other) == 0 def parse_deb_path(path): if not path.endswith('.deb'): raise ValueError('{path!r} is not a .deb file'.format(path=path)) name, version, arch = path[:-4].split('_') return name, debversion(version) def log_start(s): print(s, end=' ... ') sys.stdout.flush() def log_end(s): print(s) @contextlib.contextmanager def silenced_stdout(): stdout_fd = sys.stdout.fileno() stdout_copy_fd = os.dup(stdout_fd) try: null_fd = os.open(os.devnull, os.O_WRONLY) try: os.dup2(null_fd, stdout_fd) finally: os.close(null_fd) try: yield finally: os.dup2(stdout_copy_fd, stdout_fd) finally: os.close(stdout_copy_fd) def main(): apt_pkg.init() ap = argparse.ArgumentParser() ap.add_argument('paths', metavar='DEB-OR-CHANGES', nargs='+') ap.add_argument('--no-skip', action='store_true') ap.add_argument('--adequate', default='adequate') options = ap.parse_args() if os.getuid() != 0: print('{}: error: administrator privileges are required'.format(ap.prog), file=sys.stderr) sys.exit(1) packages = collections.defaultdict(list) for path in options.paths: if path.endswith('.changes'): with open(path, 'rt', encoding='UTF-8') as file: for para in apt_pkg.TagFile(file): for line in para['Files'].splitlines(): md5, size, section, priority, path = line.split() name, version = parse_deb_path(path) packages[name] += [(version, path)] elif path.endswith('.deb'): name, version = parse_deb_path(path) packages[name] += [(version, path)] cache = apt.cache.Cache() rc = 0 for name, versions in sorted(packages.items()): versions.sort() prev_version = None for version, path in versions: if prev_version is None: if len(versions) == 1: log_start('install {}'.format(name)) else: log_start('install {} ({})'.format(name, version)) else: log_start('upgrade {} ({} => {})'.format(name, prev_version, version)) debpkg = apt.debfile.DebPackage(path, cache) if not debpkg.check(): if options.no_skip: action = 'ERROR' rc = 1 else: action = 'SKIP' log_end('{} (not installable)'.format(action)) continue elif debpkg.missing_deps: if options.no_skip: action = 'ERROR' rc = 1 else: action = 'SKIP' log_end('{} (missing dependencies)'.format(action)) continue with silenced_stdout(): debpkg.install() cache = apt.cache.Cache() log_end('ok') log_start('check {}'.format(name)) output = ipc.check_output([options.adequate, name]) output = output.decode('ASCII').splitlines() output = sorted(output) # tag order is not deterministic if version == versions[-1][0]: [pkginfo] = cache[name].versions expected = [ '{}: {}'.format(name, line) for line in pkginfo.description.splitlines(False) if line ] else: expected = [] if output == expected: log_end('ok') else: log_end('FAIL') diff = list( difflib.unified_diff(expected, output, n=9999) ) for line in diff[3:]: print(line) rc = 1 prev_version = version log_start('remove {}'.format(name)) try: pkg = cache[name] except KeyError: log_end('not needed') else: pkg.mark_delete(purge=True) with silenced_stdout(): cache.commit() log_end('ok') cache = apt.cache.Cache() sys.exit(rc) if __name__ == '__main__': main() # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/tests/uml-guest-init0000775000000000000000000000223612104770214015502 0ustar #!/bin/sh set -e -u -x export PATH=/usr/sbin:/usr/bin:/sbin:/bin mount_stuff() { mount -n -t proc proc /proc mount -n -t sysfs sysfs /sys mount -n -t devpts devpts /dev/pts mount -n -o bind /usr/lib/uml/modules /lib/modules mount -n -t tmpfs tmpfs /tmp -o size=1G } if [ "$1" != '--cow' ] then export testdir="$(printf '%s' "$1" | tr _ = | base64 -d)" export testfile="$(printf '%s' "$2" | tr _ = | base64 -d)" mount_stuff mkdir -m 0700 /tmp/dev mknod /tmp/dev/ubda b 98 0 mkswap -f /tmp/dev/ubda swapon /tmp/dev/ubda mkdir -m 0700 /tmp/ro /tmp/rw /tmp/aufs mount -n -t hostfs hostfs /tmp/ro -o /,ro mount -n -t aufs aufs /tmp/aufs -o noatime,noxino,dirs=/tmp/rw:/tmp/ro=ro exec chroot /tmp/aufs "$0" --cow else mount_stuff rm -vf \ /var/lib/dpkg/lock \ /var/lib/dpkg/triggers/Lock \ /var/lib/apt/listchanges.db \ /var/log/apt/term.log \ /var/log/apt/history.log \ /var/log/dpkg.log \ /var/cache/apt/archives/lock \ /var/cache/debconf/*.dat cd "$testdir" ./run-tests "$testfile" : adequate tests succeeded : fi # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/debian/0000775000000000000000000000000012254521420012745 5ustar adequate-0.9.1ubuntu1/debian/tests/0000775000000000000000000000000012254521360014112 5ustar adequate-0.9.1ubuntu1/debian/tests/adequate0000775000000000000000000000030112104444034015617 0ustar #!/bin/sh set -e -u if [ "$(id -u)" != 0 ] then printf '%s: you must run this as root.\n' "$0" >&2 exit 1 fi cp -a tests/* "$ADTTMP" cd "$ADTTMP" make 2>&1 make run # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/debian/tests/control0000664000000000000000000000037712254521357015532 0ustar Tests: adequate Depends: adequate, build-essential, debhelper, python3 (>= 3.2), python3-apt, python (>= 2.7), python (<< 2.8) Restrictions: needs-root # originally had "breaks-testbed" too, but Ubuntu's "adt-virt-null in qemu" # does not support that adequate-0.9.1ubuntu1/debian/rules0000775000000000000000000000132012155051351014022 0ustar #!/usr/bin/make -f .PHONY: clean clean: doc/Makefile tests/Makefile $(MAKE) -C doc/ clean $(MAKE) -C tests/ clean dh_clean debian/build-stamp .PHONY: build build-arch build-indep build: build-indep build-indep: debian/build-stamp debian/build-stamp: doc/Makefile adequate $(MAKE) -B -C doc/ ifeq "$(filter nocheck,$(DEB_BUILD_OPTIONS))" "" ./adequate coreutils 2>&1 | ( ! grep . ) endif touch $(@) .PHONY: binary binary-arch binary-indep binary: binary-indep binary-indep: build-indep dh_testroot dh_prep dh_installdirs dh_install dh_installman doc/*.1 dh_perl dh_installdocs dh_installchangelogs dh_compress dh_fixperms dh_installdeb dh_gencontrol dh_md5sums dh_builddeb # vim:ts=4 sw=4 noet adequate-0.9.1ubuntu1/debian/source/0000775000000000000000000000000012077607271014261 5ustar adequate-0.9.1ubuntu1/debian/source/format0000664000000000000000000000001512006551124015453 0ustar 3.0 (native) adequate-0.9.1ubuntu1/debian/install0000664000000000000000000000010612006551124014332 0ustar adequate /usr/bin/ apt.conf.d /etc/apt/ templates /usr/share/debconf/ adequate-0.9.1ubuntu1/debian/NEWS0000664000000000000000000000034312077611417013455 0ustar adequate (0.3.1) experimental; urgency=low The APT hook is now disabled by default. To re-enable it, please edit the /etc/apt/apt.conf.d/20adequate file. -- Jakub Wilk Tue, 22 Jan 2013 23:30:21 +0100 adequate-0.9.1ubuntu1/debian/changelog0000664000000000000000000003166312254521420014630 0ustar adequate (0.9.1ubuntu1) trusty; urgency=medium * Merge with Debian unstable. Remaining Ubuntu changes: - debian/tests/control: Drop "breaks-testbed" restriction. Our "adt-virt-null in qemu" runner does not support that, but we want to run the test anyway. -- Martin Pitt Thu, 19 Dec 2013 08:40:33 +0100 adequate (0.9.1) unstable; urgency=low * Update the package description. * Improve the test suite: + Fix Architecture fields of test packages. + Don't hardcode the i386 architecture (closes: #732448). Thanks to Martin Pitt for the bug report and the initial patch. + Fix DEP-8 test dependencies. + Fix test failures with ld that defaults to --as-needed. Thanks to Martin Pitt for the bug report and the patch. -- Jakub Wilk Wed, 18 Dec 2013 09:25:29 +0100 adequate (0.9) unstable; urgency=low * Summary of tag changes: + Added: - program-name-collision - missing-alternative * Add license information for libgmp.so.10 and libltdl.so.7. * Check for program name collisions. * Check if providers of x-terminal-emulator and x-window-manager register required alternatives. * Test runner: don't expect deterministic order of emitted tags. * Improve error handling. * Tidy up the code. Thanks, perlcritic. -- Jakub Wilk Mon, 16 Dec 2013 14:16:11 +0100 adequate (0.8.2) unstable; urgency=low * Improve the manual page: + Fix hyphens used as minus signs. + Add examples (closes: #726152) Thanks to Shirish Agarwal for the bug report. * Don't complain about files in /usr/share/paster_templates/ not being byte-compiled. * Fix reporting incompatible-licenses, and other ELF-related tags, against wrong package (closes: #729031). * Fix incorrect implementation of --tags for ELF-related tags. -- Jakub Wilk Thu, 05 Dec 2013 10:04:22 +0100 adequate (0.8.1) unstable; urgency=medium * Fix possible privilege escalation via tty hijacking (CVE-2013-6409, closes: #730691). + Switch users only when running ldd. + Run ldd with stdin redirected to /dev/null, and without controlling terminal when run with reduced privileges. * Bump standards version to 3.9.5 (no changes needed). -- Jakub Wilk Thu, 28 Nov 2013 11:27:21 +0100 adequate (0.8) unstable; urgency=low * Summary of tag changes: + Added: - missing-symbol-version-information - symbol-size-mismatch * When printing warnings from ldd, avoid outputting binary name twice. * Check for symbol size mismatches. * Check for missing version information. * Report symbol issues with libraries in private directories (when they can be triggered by running ldd against binaries or libraries in public directories). * Add license information for libpoppler.so.37. * Don't complain about missing lua_* and luaL_* symbols in liblua* libraries. * Add “Summary of tag changes” sections to old changelog entries. -- Jakub Wilk Mon, 16 Sep 2013 13:27:06 +0200 adequate (0.7.1) unstable; urgency=low * When checking ELFs, skip unresolvable symlinks (closes: #709484, #716988). Thanks to Laurent Bonnaud and Peter Karbaliotis for the bug reports. -- Jakub Wilk Tue, 16 Jul 2013 15:37:13 +0200 adequate (0.7) unstable; urgency=low * Summary of tag changes: + Added: - incompatible-licenses * Check if Python modules in /usr/lib/pypy/ are byte-compiled (closes: #710021). * Check for binaries linked to libraries with incompatible licenses. Currently only licenses of the following libraries are recognized: OpenSSL, GnuTLS, Poppler, and GNU Readline. * Remove a duplicate label. Thanks to Niels Thykier for the bug report. * Silence “skipping because it's not installed” warnings (closes: #712446). Thanks to Paul Wise for the bug report. * Add a section about reporting bugs to the manual page. * Update the package description. * Add “XS-Testsuite: autopkgtest”. -- Jakub Wilk Tue, 25 Jun 2013 00:25:26 +0200 adequate (0.6) unstable; urgency=low * Improve error handling when resolving symlinks (see bug #709484). * Improve error handling when accessing /var/lib/adequate/pending. * Add the --tags option (closes: #709372). * Assume that executable .py files in private directories that start with shebangs are scripts, rather than Python modules, and therefore don't require bytecompilation (closes: #709192). * Disable warnings about use of smartmatch (closes: #710063). * Add a minimal build-time test that verifies that adequate runs successfully and without warnings on a simple package (coreutils). + Add “perl (>= 5.12)” to Build-Depends. * Unset all LD_* environment variables when running ldd. -- Jakub Wilk Wed, 29 May 2013 22:25:59 +0200 adequate (0.5.3) unstable; urgency=low * Make the obsolete conffile check work with pre-multi-arch dpkg. * Don't consider a conffile obsolete if dpkg-query lists it as non-obsolete and belonging to a different package (closes: #708588). This is a work-around for dpkg bug #645849. * Don't complain about /usr/lib/pythonX.Y/__phello__.foo.py not being byte-compiled. * Don't complain about .py files in /usr/share/apport/package-hooks/ not being byte-compiled (closes: #709187). -- Jakub Wilk Tue, 21 May 2013 21:10:41 +0200 adequate (0.5.2) unstable; urgency=low * Don't use dh_testdir; instead, use makefile rules to ensure that debian/rules can be only run in the correct directory. * Whitelist ps_* symbols in libthread_db-*.so libraries when checking for undefined symbols. * Improve diagnostics when ldd fails (closes: #706915). Thanks to Paul Wise for the bug report. * Find more undefined versioned symbols. * Make --pending ignore dpkg-query exit code (closes: #707080). This is needed because packages listed in /var/lib/adequate/pending might have been removed in the mean time. Thanks to Paul Wise for the bug report. * Don't complain about missing .pyc in /usr/lib/python2.X/dist-packages/ if 2.X is not a supported Python version (closes: #707614). * Don't complain about missing .pyc in /usr/lib/python3/dist-packages/ if no supported Python 3 version is installed. * Clean tests/ subdirectory in the clean target. -- Jakub Wilk Fri, 10 May 2013 12:39:39 +0200 adequate (0.5.1) unstable; urgency=low * Update the package description. * Disable the unicode_strings Perl feature. * Improve the manual page: + Call “Debian Python Policy” simply “Python Policy”. + Add links to Debian Policy and Python Policy. Thanks to Shirish Agarwal for the bug report. * Handle diversions correctly (closes: #706107). Thanks to Ben Hutchings for the bug report. * Fix Restrictions field in debian/tests/control. (The items should be space-separated, rather than comma-separated.) -- Jakub Wilk Wed, 24 Apr 2013 21:57:35 +0200 adequate (0.5) unstable; urgency=low * Summary of tag changes: + Added: - library-not-found * When looking for /(s)bin binaries, don't skip symlinks that point outside /(s)bin. * When looking for public executables and shared libraries, don't skip symlinks that point to directories that wouldn't normally be checked. * Check for binaries linked to libraries that cannot be found. * Change Debconf prompt priority to critical. -- Jakub Wilk Thu, 04 Apr 2013 22:09:12 +0200 adequate (0.4.4) unstable; urgency=low * Make sure that the APT hook won't fail when the package is removed but not purged (closes: #702904). Thanks to Andreas Beckmann for the bug report. -- Jakub Wilk Thu, 14 Mar 2013 17:50:28 +0100 adequate (0.4.3) unstable; urgency=low * Upload to unstable. -- Jakub Wilk Mon, 11 Mar 2013 22:30:21 +0100 adequate (0.4.2) experimental; urgency=low * Skip packages that are not fully installed (closes: #701995). Thanks to Axel Beckert for the bug report. * Refactor code responsible for printing errors and warnings. -- Jakub Wilk Sat, 02 Mar 2013 10:59:42 +0100 adequate (0.4.1) experimental; urgency=low * Update the package description. * Add description to the manual page. * Document --apt-preinst and --pending in more details. * Add a test suite. + Add DEP-8 test script. * Add README.source asking to change source format when forking. * Improve the pyshared-file-not-bytecompiled tag description. * Fix Python byte-compilation checks for Python < 2.6. * Fix Python byte-compilation checks for python-support < 0.90. * Improve error handling. -- Jakub Wilk Thu, 21 Feb 2013 13:41:14 +0100 adequate (0.4) experimental; urgency=low * Detect missing versioned symbols, too. * Avoid running ldd against the following files: - files that are known not to be readable; - dynamic linkers; - files with whitespace in their names. * Filter package names through dpkg-query. This adds or removes architecture suffixes, as needed. * Reduce number of ldd(1) calls to at most two. * Add option for chroot(2)ing into another directory: --root. * Add option for switching user and group: --user. * Make the APT hook run as user ‘nobody’. * Make the package more portable: + Stop requiring multi-arch-aware dpkg. Drop versioned runtime dependency on dpkg. + Stop using IPC::Run. Drop libipc-run-perl recommendation. + Load debconf modules lazily. * Improve options parsing. Exit with error for incompatible combinations of options (e.g. both --pending and --all). * Make --help more verbose. * Improve the documentation: + Work around bug #280148 in pod2man: manual page references should be rendered in bold, not in italics. + Add a to-do list. + Rewrite the undefined-symbol tag description. + Don't repeat “Debian Policy” if a tag refers multiple policy sections. + Document the --help option. * Replace “underlinked libraries” with “undefined symbols” in the package description. The latter is what adequate actually detects, while the former might or might not be the root cause of the observed problem. * Make generating the POD file atomic. -- Jakub Wilk Wed, 30 Jan 2013 23:46:24 +0100 adequate (0.3.1) experimental; urgency=low * Initial upload to Debian (closes: #698656). * Update years in debian/copyright. * Disable the APT hook. (It can be re-enabled by editing /etc/apt/apt.conf.d/20adequate). * Improve error handling. * Improve the documentation. * Rebuild the documentation from source. * Add Vcs-* and Homepage fields. -- Jakub Wilk Tue, 22 Jan 2013 23:30:21 +0100 adequate (0.3) unstable; urgency=low * Summary of tag changes: + Added: - undefined-symbol * Improve the documentation. * Remove some no-op code. * Check for undefined symbols. * Add libipc-run-perl to Recommends (needed for the new check). * Update the package description. * Bump standards version to 3.9.4 (no changes needed). -- Jakub Wilk Mon, 21 Jan 2013 20:25:37 +0100 adequate (0.2.1) unstable; urgency=low * Don't expect third-party Python 2.X modules to be byte-compiled if the corresponding Python version is not installed. * Print symlink target when reporting broken symlink. * Provide a manual page. -- Jakub Wilk Fri, 14 Sep 2012 17:32:13 +0200 adequate (0.2) unstable; urgency=low * Summary of tag changes: + Added: - bin-or-sbin-binary-requires-usr-lib-library * Don't complain about .py files in /usr/lib/debug/usr/bin/ etc. * Don't complain about .py files in /usr/games/. * Check for /bin and /sbin binaries requiring /usr/lib libraries. * Reduce number of dpkg -L calls to one. -- Jakub Wilk Mon, 03 Sep 2012 20:27:04 +0200 adequate (0.1.1) unstable; urgency=low * Summary of tag changes: + Added: - pyshared-file-not-bytecompiled * Don't store ambiguous package names (for Multi-Arch: same packages) in /var/lib/adequate/pending. * Don't complain about .py files in /etc that are not byte-compiled. * Don't complain about .py files within standard libraries of alternative Python interpreters (jython, pypy) that are not byte-compiled. * Check byte-compilation of files in /usr/share/pyshared/ and /usr/share/python-support/. * Fix emitting messages longer than 2 lines via debconf. * Don't put colon between tag name and extra information. -- Jakub Wilk Sat, 11 Aug 2012 18:51:40 +0200 adequate (0.1) unstable; urgency=low * Initial release. * Summary of tag changes: + Added: - broken-symlink - missing-copyright-file - obsolete-conffile - py-file-not-bytecompiled -- Jakub Wilk Thu, 02 Aug 2012 21:19:14 +0200 adequate-0.9.1ubuntu1/debian/prerm0000664000000000000000000000014712006551124014016 0ustar #!/bin/sh set -e #DEBHELPER# [ $1 = upgrade ] || rm -f /var/lib/adequate/pending # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/debian/dirs0000664000000000000000000000002312006551124013623 0ustar /var/lib/adequate/ adequate-0.9.1ubuntu1/debian/README.source0000664000000000000000000000034112103557225015127 0ustar If you fork adequate (NMU, backport, derivative-specific change, etc.), please consider changing the source format to “3.0 (quilt)”. Thanks in advance! -- Jakub Wilk Sun, 03 Feb 2013 23:00:48 +0100 adequate-0.9.1ubuntu1/debian/copyright0000664000000000000000000000226212077317577014725 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: * Copyright: 2012, 2013 Jakub Wilk License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. adequate-0.9.1ubuntu1/debian/control0000664000000000000000000000204212254521441014351 0ustar Source: adequate Section: utils Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Jakub Wilk Build-Depends: debhelper (>= 7), perl (>= 5.12), python3 (>= 3.2), python3-apt Standards-Version: 3.9.5 Vcs-Hg: https://bitbucket.org/jwilk/adequate Vcs-Browser: https://bitbucket.org/jwilk/adequate Homepage: http://jwilk.net/software/adequate XS-Testsuite: autopkgtest Package: adequate Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, perl (>= 5.12), debconf Description: Debian package quality testing tool adequate checks packages installed on the system and reports bugs and policy violations. . The following checks are currently implemented: * broken symlinks; * missing copyright file; * obsolete conffiles; * Python modules not byte-compiled; * /bin and /sbin binaries requiring /usr/lib libraries; * missing libraries, undefined symbols, symbol size mismatches; * license conflicts; * program name collisions; * missing alternatives. adequate-0.9.1ubuntu1/debian/compat0000664000000000000000000000000212006551124014142 0ustar 7 adequate-0.9.1ubuntu1/private/0000775000000000000000000000000012253556054013207 5ustar adequate-0.9.1ubuntu1/private/update-coverage0000775000000000000000000000462412106223110016173 0ustar #!/usr/bin/python3 # Copyright © 2013 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import os import collections import apt_pkg def main(): base = os.path.join(os.path.dirname(__file__), os.pardir) tags = {} filename = '{base}/doc/tags.desc'.format(base=base) with open(filename, 'rt', encoding='UTF-8') as file: for section in apt_pkg.TagFile(file): tag = section['tag'] tags[tag] = 0 filename = '{base}/tests/testpkg/debian/control'.format(base=base) with open(filename, 'rt', encoding='UTF-8') as file: for n, section in enumerate(apt_pkg.TagFile(file)): if n == 0: continue description = section['description'] emitted_tags = frozenset( line.split()[0] for line in description.splitlines()[1:] ) for tag in emitted_tags: tags[tag] += 1 filename = '{base}/tests/coverage.txt'.format(base=base) with open(filename, 'wt', encoding='UTF-8') as file: for tag, n in sorted(tags.items()): if n == 0: checkbox = '[ ]' elif n == 1: checkbox = '[x]' else: checkbox = '[{n}]'.format(n=n) print('{c} {tag}'.format(c=checkbox, tag=tag), file=file) if __name__ == '__main__': main() # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/private/generate-manpage0000775000000000000000000000673412253556054016347 0ustar #!/usr/bin/python3 # Copyright © 2012, 2013 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import io import os import re import subprocess as ipc import sys import apt_pkg def podify_reference(s): s = s.strip() s = re.sub(r'^([a-z]+://\S+)$', r'>', s) s = re.sub(r'^(.*)\((\d+)\)$', r'B>(\2)', s) return s def podify_synopsis(s): if s.startswith(' '): s = s.lstrip() s = re.sub(r'<(.+?)>', r'I<\1>', s) s = re.sub(r'^(\S+)', r'B<\1>', s) s += '\n' return s def podify_description(s): s = re.sub(r'^\s+', '', s, flags=re.MULTILINE) s = re.sub(r'<(.+?)>', r'I<\1>', s) s = re.sub(r'`(.+?)`', r'C<\1>', s) return s def fix_markup(s): # : # manpage references should be rendered in bold, not in italics s = re.sub('L<(\w+)[(](\d)[)]>', r'B>(\2)', s) s = re.sub('L<([a-z]+://\S+)>', r'>', s) return s def main(): sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='UTF-8') base = os.path.join(os.path.dirname(__file__), os.pardir) desc_fn = os.path.join(base, 'doc', 'tags.desc') tmpl_fn = os.path.join(base, 'doc', 'adequate.podt') code_fn = os.path.join(base, 'adequate') with open(tmpl_fn, 'rt', encoding='UTF-8') as file: for line in file: line = line.rstrip() if line == '=head1 TAGS': output_tags(desc_fn) else: line = fix_markup(line) print(line) def output_tags(desc_fn): print('=head1 TAGS') print() print('=over 4') print() with open(desc_fn, 'rt', encoding='UTF-8') as file: for section in apt_pkg.TagFile(file): tag = section['tag'] print('=item B>'.format(tag=tag)) print() try: description = section['description'] except LookupError: pass else: description = podify_description(description) print(description) print() try: references = section['references'] except LookupError: pass else: references = ', '.join(podify_reference(s) for s in references.splitlines()) print('References: {refs}.'.format(refs=references)) print() print('=back') if __name__ == '__main__': main() # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/private/update-perlcritic0000775000000000000000000000045012253556054016554 0ustar #!/bin/sh set -e -u here=$(basename "$0") cd "$here/.." target=doc/todo.perlcritic if perlcritic --statistics-only adequate > "${target}.new" then : else rc=$? [ $rc -eq 2 ] || exit $rc fi diff -u "${target}" "${target}.new" || true mv "${target}.new" "${target}" # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/private/update-tags0000775000000000000000000000552512155051351015351 0ustar #!/usr/bin/python3 # Copyright © 2012, 2013 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import os import re import sys import apt_pkg tag_re = re.compile(r"^:\s+Tags[(]qw[(](.+?)[)][)]") required_fields = {'Tag', 'Description'} known_fields = required_fields | {'References'} def main(): base = os.path.join(os.path.dirname(__file__), os.pardir) desc_fn = os.path.join(base, 'doc', 'tags.desc') code_fn = os.path.join(base, 'adequate') tags = {} with open(desc_fn, 'rt', encoding='UTF-8') as file: for section in apt_pkg.TagFile(file): tag = section['tag'] tags[tag] = section for field in required_fields: if field not in section: print('W: {tag}: required field {field} is missing'.format(tag=tag, field=field), file=sys.stderr) for field in section.keys(): if field not in known_fields: print('W: {tag}: unknown field {field}'.format(tag=tag, field=field), file=sys.stderr) obsolete_tags = set(tags) with open(code_fn, 'rt', encoding='UTF-8') as file: for line in file: match = tag_re.match(line) if match is None: continue for tag in match.group(1).split(): if tag in obsolete_tags: obsolete_tags.remove(tag) if tag in tags: continue tags[tag] = 'Tag: {tag}\n'.format(tag=tag) with open(desc_fn + '+', 'wt', encoding='UTF-8') as file: for tag, section in sorted(tags.items()): print(section, file=file) os.rename(desc_fn + '+', desc_fn) for tag in sorted(obsolete_tags): print('W: {tag}: obsolete tag'.format(tag=tag), file=sys.stderr) if __name__ == '__main__': main() # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/adequate0000775000000000000000000010143512253576321013257 0ustar #!/usr/bin/perl # Copyright © 2012, 2013 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. use strict; use warnings; use v5.12; # for delete local no feature 'unicode_strings'; no if $] >= 5.018, warnings => 'experimental::smartmatch'; use Attribute::Handlers; use Cwd; use English qw(-no_match_vars); use Getopt::Long qw(:config); use Errno; use Fcntl qw(:flock); use IO::Handle qw(); use POSIX qw(setsid); BEGIN { $ENV{'DEBCONF_NOWARNINGS'} = 'yes'; ## no critic (RequireLocalizedPunctuationVars) local %::known_tags = (); local %::visible_tags = (); } my $pending_path = '/var/lib/adequate/pending'; my %pending = (); my $pending_fh; sub flush_std_fh { IO::Handle::flush(*STDOUT) or die $!; IO::Handle::flush(*STDERR) or die $!; return; } sub read_pending { die if defined $pending_fh; if (open($pending_fh, '+>>', $pending_path)) { ## no critic (RequireBriefOpen) flock $pending_fh, LOCK_EX or die "$pending_path: $!"; seek($pending_fh, 0, 0) or die "$pending_path: $!"; while (<$pending_fh>) { chomp; $pending{$_} = 1; } } elsif ($!{ENOENT}) { return; } else { die "$pending_path: $!"; } return; } sub write_pending { defined $pending_fh or die; truncate($pending_fh, 0) or die "$pending_path: $!"; seek($pending_fh, 0, 0) or die "$pending_path: $!"; for (sort keys %pending) { print {$pending_fh} "$_\n" or die "$pending_path: $!"; } close $pending_fh or die "$pending_path: $!"; $pending_fh = undef; return; } sub do_apt_preinst { my $enabled = undef; while () { given ($_) { when ("Adequate::Enabled=true\n") { $enabled = 1; } when ("Adequate::Enabled=false\n") { $enabled = 0; } when ("\n") { last; } } } if (not defined $enabled) { warning('apt hook is not enabled'); } if (not $enabled) { return; } while () { my ($package, $architecture) = m{^(\S+) \s+ \S+ \s+ \S+ \s+ \S+ \s+ /.+_([a-z0-9]+)[.]deb$}x or next; $package = "$package:$architecture" if $architecture ne 'all'; $pending{$package} = 1; } write_pending(); return; } sub do_pending { process(1, keys %pending) if %pending; %pending = (); write_pending(); return; } my $use_debconf = 0; my @debconf_buffer = (); my $ldd_uid = undef; my $ldd_gid = undef; sub process { my ($ignore_missing, @packages) = @_; my %package_map = get_package_map($ignore_missing, @packages); @packages = keys %package_map; if (not @packages) { if ($ignore_missing) { return; } else { error('no packages to check'); } } my %file_map = get_file_map(@packages); check_broken_symlinks(%file_map); check_copyright(@packages); check_obsolete_conffiles(@packages); check_python_bytecompilation(%file_map); check_elfs(%file_map); check_paths(%file_map); check_alternatives(\%package_map, \%file_map); flush_debconf(); return; } sub debconf { my ($subname, @args) = @_; no strict qw(refs); ## no critic (ProhibitNoStrict) my $sub = \&{"Debconf::Client::ConfModule::$subname"}; my ($rc, $msg) = $sub->(@args); if ($rc != 0) { die "interaction with debconf failed: $msg"; } } sub flush_debconf { @debconf_buffer or return; my $debconf_buffer = join("\n", @debconf_buffer); $debconf_buffer =~ s/\\/\\\\/g; $debconf_buffer =~ s/\n/\\n/g; my $t = 'adequate/error'; debconf('version', '2.0'); debconf('capb', 'escape'); debconf('fset', $t, 'seen', 0); debconf('subst', $t, 'tags', $debconf_buffer); debconf('input', 'critical', $t); debconf('title', 'adequate found packaging bugs'); debconf('go'); return; } sub tag { my ($pkg, $tag, @extra) = @_; die "attempted to emit unknown tag $tag" if not defined $::known_tags{$tag}; $::visible_tags{$tag} or return; if ($use_debconf) { push @debconf_buffer, "$pkg: $tag @extra"; } elsif (-t STDOUT) { print "$pkg: \e[31m$tag\e[0m @extra\n" or die $!; } else { print "$pkg: $tag @extra\n" or die $!; } return; } sub get_package_map { my ($ignore_dpkg_query_errors, @packages) = @_; my %map; flush_std_fh(); open(my $fh, '-|', 'dpkg-query', '-Wf', '${binary:Package} ${Package};${Status};${Provides}\n', ## no critic (RequireInterpolation) # try both ${binary:Package} and ${Package}; the former gives us # architecture information, but the later works with pre-multiarch dpkg '--', @packages ) or die "dpkg-query -W: $!"; while (<$fh>) { my ($package, $status, $provides) = m/^\s*(\S+).*;.*\s(\S+);(.*)$/; if ($status eq 'installed') { my %provides = map { $_ => 1 } split(m/,\s*/, $provides); $map{$package} = \%provides; } elsif (@packages) { info("skipping $package because it's not installed"); } } close($fh) or $ignore_dpkg_query_errors or die 'dpkg-query -W: ' . ($! or 'failed'); return %map; } sub get_file_map ## no critic (RequireArgUnpacking) { my %map = (); flush_std_fh(); open(my $fh, '-|', 'dpkg', '-L', @_) or die "dpkg -L: $!"; my $pkg = shift; $map{$pkg} = []; while (<$fh>) { if (/^$/) { ## no critic (ProhibitFixedStringMatches) $pkg = shift; $map{$pkg} = []; next; } if (m{^(?:locally diverted|diverted by \S+) to: (/.+)$}) { $map{$pkg}->[-1] = $1; next; } m{^(/.+)$} or next; push($map{$pkg}, $1); } close($fh) or die 'dpkg -L: ' . ($! or 'failed'); return %map; } sub get_alternative_map { my %map = (); local $ENV{LC_ALL} = 'C'; flush_std_fh(); open(my $fh, '-|', 'update-alternatives', '--get-selections') or die 'update-alternatives --get-selections: ' . ($! or 'failed'); while (<$fh>) { my ($alt, $path) = m/^(\S+)\s+\S+\s+(\S+)$/ or die 'unexpected output from update-alternatives --get-selections'; $map{$alt}{$path} = 1; } close($fh) or die 'update-alternatives --get-selections: ' . ($! or 'failed'); return %map; } sub UNIVERSAL::Tags : ATTR(CODE) { my (undef, $symbol, $code, undef, $tags) = @_; for my $tag (@{$tags}) { $::known_tags{$tag} = 1; } no warnings qw(redefine); ## no critic (ProhibitNoWarnings) *{$symbol} = sub { local %::visible_tags = map { $_ => 1 } grep { exists $::visible_tags{$_} } @{$tags}; return $code->(@_) if %::visible_tags; return; }; return; } sub check_broken_symlinks : Tags(qw(broken-symlink)) { my %map = @_; while (my ($pkg, $files) = each %map) { for my $file (@{$files}) { if (-l $file and not stat($file)) { my $target = readlink $file; if (defined $target) { tag $pkg, 'broken-symlink', $file, '->', $target; } else { tag $pkg, 'broken-symlink', $file, "($!)"; } } } } return; } sub check_copyright : Tags(qw(missing-copyright-file)) { my @packages = @_; for my $pkg (@packages) { $pkg =~ s/:.*//; my $file = "/usr/share/doc/$pkg/copyright"; if (! -f $file) { tag $pkg, 'missing-copyright-file', $file; } } return; } sub check_obsolete_conffiles : Tags(qw(obsolete-conffile)) { my @packages = @_; my $pkg; flush_std_fh(); open(my $fh, '-|', 'dpkg-query', '-Wf', '${binary:Package},${Package}\n${Conffiles}\n', ## no critic (RequireInterpolation) # try both ${binary:Package} and ${Package}; the former gives us # architecture information, but the later works with pre-multiarch dpkg ) or die "dpkg-query -W: $!"; my %file2obs = (); my %pkg2files = (); while (<$fh>) { if (m/^,?([^,\s]+)/) { $pkg = $1; } elsif (m{^ (.*) [0-9a-f]+( obsolete)?$}) { my $file = $1; my $obsolete = defined $2; defined $pkg or die 'unexpected output from dpkg-query -W'; if ($obsolete) { $file2obs{$file} //= 1; my $files = $pkg2files{$pkg} //= []; push @{$files}, $file; } else { # Work-around for dpkg bug #645849: don't consider a conffile # obsolete if it's listed as non-obsolete in a different # package. $file2obs{$file} = 0; } } } close($fh) or die 'dpkg-query -W: ' . ($! or 'failed'); for my $pkg (@packages) { my $files = $pkg2files{$pkg} // []; defined $files or die; for my $file (@{$files}) { if ($file2obs{$file}) { tag $pkg, 'obsolete-conffile', $file; } } } return; } sub get_python_versions { my @group = (undef, undef); for my $version (2..3) { my @result = (); my $path = "/usr/share/python$version/debian_defaults"; $path =~ s{/python\K2/}{/}; if (open(my $fh, '<', $path)) { while (<$fh>) { if (/^supported-versions\s*=\s*(\S.+\S)\s*$/) { my $versions = $1; push @result, grep { -f "/usr/lib/$_/os.py" } split(/\s*,\s*/, $versions); last; } } close($fh) or die "$path: $!"; } elsif (not $!{ENOENT}) { die "$path: $!"; } push @group, \@result; } return @group; } my $bytecompilation_not_needed_re = qr{ etc/ | bin/ | sbin/ | usr/bin/ | usr/games/ | usr/lib/debug/bin/ | usr/lib/debug/sbin/ | usr/lib/debug/usr/bin/ | usr/lib/debug/usr/games/ | usr/lib/debug/usr/sbin/ | usr/lib/pypy/lib-python/\d[.]\d+/test/bad | usr/lib/pypy/lib-python/\d[.]\d+/lib2to3/tests/data/ | usr/sbin/ | usr/share/apport/package-hooks/ | usr/share/doc/ | usr/share/jython/ | usr/share/paster_templates/ | usr/lib/python\d[.]\d+/__phello__[.]foo[.]py$ }x; # Please keep it in sync with lintian4python! sub check_python_bytecompilation : Tags(qw(pyshared-file-not-bytecompiled py-file-not-bytecompiled)) { my %map = @_; my @pythons = get_python_versions(); my @python2s = @{$pythons[2]}; my @python3s = @{$pythons[3]}; my $pypy_installed = -f '/usr/bin/pypy'; my $pysupport_old = -d '/usr/lib/python-support/private/'; # python-support < 0.90 my $pysupport_new = -d '/usr/share/python-support/private/'; # python-support >= 0.90 while (my ($pkg, $files) = each %map) { file: for (@{$files}) { my ($path, $dir, $base) = m{^((/.+/)([^/]+)[.]py)$} or next; next file if m{^/$bytecompilation_not_needed_re}; if (m{^/usr/share/pyshared/(.+)} or m{^/usr/share/python-support/[^/]+/(? 0x04, 'GPLv3' => 0x08, 'GPLv2+' => 0x0c, 'GPLv3+' => 0x08, 'LGPLv2.1' => 0x14c, 'LGPLv3' => 0x188, 'LGPLv2.1+' => 0x1cc, 'LGPLv3+' => 0x188, 'OpenSSL' => 0x100, ); my %soname2license = ( 'libcrypto.so.0.9.8' => 'OpenSSL', 'libcrypto.so.1.0.0' => 'OpenSSL', 'libgmp.so.10' => 'GPLv3+', 'libgnutls-extra.so.26' => 'GPLv3+', 'libgnutls-openssl.so.27' => 'GPLv3+', 'libgnutls.so.26' => 'LGPLv3+', 'libgnutls.so.28' => 'LGPLv3+', 'libltdl.so.7' => 'GPLv2+', 'libpoppler.so.19' => 'GPLv2', 'libpoppler.so.28' => 'GPLv2', 'libpoppler.so.37' => 'GPLv2', 'libpoppler.so.5' => 'GPLv2', 'libreadline.so.5' => 'GPLv2+', 'libreadline.so.6' => 'GPLv3+', 'libssl.so.0.9.8' => 'OpenSSL', 'libssl.so.1.0.0' => 'OpenSSL', ); sub is_inside_directories { my ($path, $dirs) = @_; my $realpath = Cwd::realpath($path) // die "resolving $path failed: $!"; my ($realdir) = $realpath =~ m{(.*)/[^/]+$}; if (defined $dirs->{$realdir}) { return $realpath; } else { return; } } sub augmented_path { my ($orig_path, $path, $interesting_dirs) = @_; if ($orig_path eq $path) { return $path; } my $realpath = Cwd::realpath($path) // die "resolving $path failed: $!"; my ($realdir) = $realpath =~ m{(.*)/[^/]+$}; # If the symlink target is still in an “interesting” directory, # then any issue hopefully will be reported against another # package. return if defined $interesting_dirs->{$realdir}; return "$orig_path => $realpath"; } sub check_elfs : Tags(qw(bin-or-sbin-binary-requires-usr-lib-library undefined-symbol symbol-size-mismatch missing-symbol-version-information library-not-found incompatible-licenses)) { my %map = @_; my @ld_vars = grep { /^LD_/ } keys %ENV; delete local @ENV{@ld_vars}; local $ENV{LC_ALL} = 'C'; my %interesting_dirs = ( '/bin' => 1, '/sbin' => 1, ); if ([keys %::visible_tags] ~~ ['bin-or-sbin-binary-requires-usr-lib-library']) { # /usr/* and ldconfing paths are not interesting in this case. } else { %interesting_dirs = (%interesting_dirs, '/usr/bin' => 1, '/usr/games' => 1, '/usr/sbin' => 1, ); flush_std_fh(); open(my $ldconfig, '-|', '/sbin/ldconfig', '-p') or die "ldconfig -p: $!"; while (<$ldconfig>) { if (m{\s[(]libc[^)]+[)]\s+=>\s+(\S+)[/][^/]+$}) { $interesting_dirs{$1} = 1; } } close($ldconfig) or die 'ldconfig -p: ' . ($! or 'failed'); } my %path2pkg = (); my %path_on_rootfs = (); while (my ($pkg, $files) = each %map) { file: for my $path (@{$files}) { my ($dir) = $path =~ m{(.*)/[^/]+$}; next file if $path =~ /\s/; next file if $path =~ m{^/lib\d*/.*(?<=/)ld(?:-.+)[.]so(?:$|[.])}; # dynamic linker defined $interesting_dirs{$dir} or next file; my $on_rootfs = $path =~ m{^/s?bin/\S+$}; -f -r $path or next file; if (-l $path) { my $realpath = Cwd::realpath($path) // die "resolving $path failed: $!"; my ($realdir) = $realpath =~ m{(.*)/[^/]+$}; # If the symlink target is still in an “interesting” directory, # then any issue hopefully will be reported against another # package. next file if defined $interesting_dirs{$realdir}; $on_rootfs &&= $realpath =~ m{^/s?bin/\S+$} } $path2pkg{$path} = $pkg; $path_on_rootfs{$path} = 1 if $on_rootfs; } } my $path = undef; my $pkg = undef; my $on_rootfs = undef; my $depends = {}; my @licenses = (); my $license_id_product = -1; my %license_conflicts = (); given (scalar keys %path2pkg) { when (0) { # nothing to do return; } when (1) { # ldd won't print the path, so let's save it here ($path, $pkg) = each %path2pkg; (undef, $on_rootfs) = each %path_on_rootfs; } } flush_std_fh(); my $ldd_pid = open(my $ldd, '-|') // die "can't fork: $!"; if ($ldd_pid) { # parent my $all_dynamic = 1; my $suspected_error = 0; foreach (<$ldd>) { when (m/^(\S+):$/) { $path = $1; $pkg = $path2pkg{$path}; $on_rootfs = $path_on_rootfs{$path}; $depends = {}; @licenses = (); $license_id_product = -1; defined $pkg or die 'unexpected output from ldd'; } when (m/^\s+not a dynamic executable$/) { $all_dynamic = 0; } when (m/^\s+statically linked$/) { # skip } when (m/^undefined symbol:\s+(\S+)(?:,\s+version\s+(\S+))?\s+[(](\S+)[)]$/) { my $symbol = $1; if (defined $2) { $symbol = "$symbol\@$2"; } my $triggering_path = $3; next if $path =~ m/python|py[23]/ and $symbol =~ /^_?Py/; next if $path =~ m/perl/ and $symbol =~ /^(?:Perl|PL)_/; next if $path =~ m{/liblua} and $symbol =~ /^luaL?_/; next if $path =~ m{/libthread_db-[0-9.]+[.]so$} and $symbol =~ /^ps_/; my $augmented_path = augmented_path($path, $triggering_path, \%interesting_dirs); defined $augmented_path or next; tag $pkg, 'undefined-symbol', $augmented_path, '=>', $symbol; } when (m/^symbol (\S+), version (\S+) not defined in file (\S+) with link time reference\s+[(](\S+)[)]/) { my $symbol = "$1\@$2"; my $lib = $3; my $triggering_path = $4; my $augmented_path = augmented_path($path, $triggering_path, \%interesting_dirs); defined $augmented_path or next; tag $pkg, 'undefined-symbol', $augmented_path, '=>', $symbol, "($lib)"; } when (m/^(\S+): Symbol `(\S+)' has different size in shared object, consider re-linking$/) { next if $path ne $1; my $symbol = $2; tag $pkg, 'symbol-size-mismatch', $path, '=>', $symbol; } when (m/^(\S+): (\S+): no version information available [(]required by (\S+)[)]$/) { my $path = $1; ## no critic (ProhibitReusedNames) my $lib = $2; my $triggering_path = $3; my $augmented_path = augmented_path($path, $triggering_path, \%interesting_dirs); defined $augmented_path or next; tag $pkg, 'missing-symbol-version-information', $augmented_path, '=>', $lib; } when (m/^\t(\S+) => not found$/) { tag $pkg, 'library-not-found', $path, '=>', $1; } when (m{^\t(\S+) => (\S+) [(]0x[0-9a-f]+[)]$}) { my ($soname, $sopath) = ($1, $2); if ($on_rootfs and $sopath =~ m{^/usr/lib/}) { tag $pkg, 'bin-or-sbin-binary-requires-usr-lib-library', $path, '=>', $sopath; } my $realsopath = is_inside_directories($sopath, \%interesting_dirs); if (defined $realsopath) { $depends->{$realsopath} = 1; } my $license = $soname2license{$soname}; if (defined $license) { my $license_id = $license2id{$license} or die "unknown license $license"; my $new_license_id_product = $license_id_product & $license_id; if ($license_id_product != $new_license_id_product) { push @licenses, [$soname, $license]; $license_id_product = $new_license_id_product; if ($license_id_product == 0) { # Don't emit incompatible-licenses tag yet, because # the conflict might have been caused by one of the # dependencies. my @tagdata = ($pkg, $path, join(' + ', map { "$_->[1] ($_->[0])" } @licenses) ); $license_conflicts{$path} = [$depends, @tagdata]; } } } } when (m/^\t(?:\S+)\s.*(?<=\s)[(]0x[0-9a-f]+[)]$/) { # skip } when (m/^ldd: /) { $suspected_error = 1; s/^ldd:\s+//; chomp; warning("ldd -r $path: $_"); } default { s/^\s+//; s/^\Q$path\E:\s+//; chomp; warning("ldd -r $path: $_"); } } wait or die "ldd -r: $!"; if ($? == 0) { # okay! } elsif (not $all_dynamic and not $suspected_error and $? == (1 << 8)) { # also okay! } else { die 'ldd -r: failed'; } close $ldd; ## no critic (RequireCheckedSyscalls) } else { # child open(STDIN, '<', '/dev/null') or die "can't redirect stdin: $!"; open(STDERR, '>&STDOUT') or die "can't redirect stderr: $!"; switch_uid_gid($ldd_uid, $ldd_gid); exec('ldd', '-r', sort keys %path2pkg); die "can't exec ldd: $!"; } my %dependency_licenses = (); for my $path (keys %license_conflicts) { for my $sopaths ($license_conflicts{$path}->[0]) { for my $sopath (keys %{$sopaths}) { $dependency_licenses{$sopath} = -1; } } } given (scalar keys %dependency_licenses) { when (0) { # nothing to do return; } when (1) { # ldd won't print the path, so let's save it here ($path, undef) = each %dependency_licenses; } } flush_std_fh(); $ldd_pid = open($ldd, '-|') // die "can't fork: $!"; if ($ldd_pid) { # parent my $suspected_error = 0; foreach (<$ldd>) { when (m/^(\S+):$/) { $path = $1; $license_id_product = -1; defined $pkg or die 'unexpected output from ldd'; } when (m{^\t(\S+) => (\S+) [(]0x[0-9a-f]+[)]$}) { my ($soname, $sopath) = ($1, $2); my $license = $soname2license{$soname}; if (defined $license) { my $license_id = $license2id{$license} or die "unknown license $license"; $dependency_licenses{$path} &= $license_id; } } } wait or die "ldd -r: $!"; if ($? != 0) { die 'ldd -r: failed'; } close $ldd; ## no critic (RequireCheckedSyscalls) } else { # child open(STDIN, '<', '/dev/null') or die "can't redirect stdin: $!"; open(STDERR, '>&STDOUT') or die "can't redirect stderr: $!"; switch_uid_gid($ldd_uid, $ldd_gid); exec('ldd', sort keys %dependency_licenses); die "can't exec ldd: $!"; } file: while (my ($path, $license_conflict) = each %license_conflicts) { ## no critic (ProhibitReusedNames) my ($sopaths, $pkg, @tagdata) = @{$license_conflicts{$path}}; ## no critic (ProhibitReusedNames) for my $sopath (keys %{$sopaths}) { next file if $dependency_licenses{$sopath} == 0; } tag $pkg, 'incompatible-licenses', @tagdata; } return; } sub check_paths : Tags(qw(program-name-collision)) { my %map = @_; my @dirs = qw(/usr/sbin /usr/bin /sbin /bin /usr/games); while (my ($pkg, $files) = each %map) { my %files = map { $_ => 1 } @{$files}; for my $sfile (@{$files}) { for my $sdir (@dirs) { $sfile =~ qr{^$sdir/(.*)} or next; -f $sfile or next; my $suffix = $1; for my $ddir (@dirs) { my $dfile = "$ddir/$suffix"; -f $dfile or next; next if exists $files{$dfile}; tag $pkg, 'program-name-collision', $sfile, $dfile; } last; } } } return; } sub check_alternatives : Tags(qw(missing-alternative)) { my ($package_map, $file_map) = @_; my %providers; while (my ($pkg, $provides) = each %{$package_map}) { for my $vpkg (qw(x-window-manager x-terminal-emulator)) { if ($provides->{$vpkg}) { push @{$providers{$vpkg}}, $pkg; } } } %providers or return; my %alternative_map = get_alternative_map(); while (my ($vpkg, $pkgs) = each %providers) { my @registered_paths = keys ($alternative_map{$vpkg} // {}); for my $pkg (@{$pkgs}) { my $files = $file_map->{$pkg}; my $found = 0; if (@registered_paths) { for my $file (@{$files}) { if ($file ~~ @registered_paths) { $found = 1; last; } } } if (not $found) { tag $pkg, 'missing-alternative', $vpkg; } } } return; } sub switch_uid_gid { my ($uid, $gid) = @_; defined $uid or return; defined $gid or return; # If the child process had a controlling terminal, the user we switch to # could take over the process with ptrace(2), and then hijack the terminal # using TIOCSTI. setsid() or die; # Similarly, if the child process inherited an fd of an open terminal, the # user could do nefarious things with the terminal. die if -t STDIN; die if -t STDOUT; die if -t STDERR; # (There might be other fds open at this point, but Perl conveniently # closes them for us on exec.) ## no critic (RequireLocalizedPunctuationVars) $! = 0; $GID = $gid; die "setting real gid to $gid: $!" if $!; $EGID = "$gid $gid"; die "setting effective gid to $gid: $!" if $!; $UID = $uid; die "setting real uid to $uid: $!" if $!; $EUID = $uid; die "setting effective uid to $uid: $!" if $!; ## use critic die if $UID != $uid; die if $EUID != $uid; die if $GID ne "$gid $gid"; die if $EGID ne "$gid $gid"; delete $ENV{HOME}; return; } sub display_help { print <<'EOF' usage: adequate [options] ... adequate [options] --all adequate [options] --apt-preinst adequate [options] --pending adequate --help options: --all check all installed packages --tags [,...] emit only these tags --tags -[,...] don't emit these tags --debconf report issues via debconf --root switch root directory --user [:] switch user and group --apt-preinst (used internally of the APT hook) --pending (used internally of the APT hook) --help display this help and exit EOF or die $!; exit(0); } sub error { say {*STDERR} "adequate: error: @_" or die $!; exit(1); } sub warning { say {*STDERR} "adequate: @_" or die $!; return; } sub info { say {*STDERR} "adequate: @_" or die $! if 0; return; } my @ARGV_copy = @ARGV; sub enable_debconf { $use_debconf = 1; if (not exists $ENV{DEBIAN_HAS_FRONTEND}) { @ARGV = @ARGV_copy; ## no critic (RequireLocalizedPunctuationVars) # import will re-exec this program } require Debconf::Client::ConfModule; Debconf::Client::ConfModule::import(); return; } umask 022; my $opt_all = 0; my $opt_tags = undef; my $opt_debconf = 0; my $opt_root = undef; my $opt_user = undef; my $opt_apt_preinst = 0; my $opt_pending = 0; my $rc = GetOptions( 'all' => \$opt_all, 'tags=s' => \$opt_tags, 'debconf' => \$opt_debconf, 'root=s' => \$opt_root, 'user=s' => \$opt_user, 'apt-preinst' => \$opt_apt_preinst, 'pending' => \$opt_pending, 'help' => \&display_help, ); if (not $rc) { exit(1); } %::visible_tags = %::known_tags; if (defined $opt_tags) { my $negative; if ($opt_tags =~ s/^-//) { $negative = 1; } else { $negative = 0; %::visible_tags = (); } my @tags = split(m/,/, $opt_tags); for my $tag (@tags) { if (not $::known_tags{$tag}) { error("unknown tag $tag"); } if ($negative) { delete $::visible_tags{$tag}; } else { $::visible_tags{$tag} = 1; } } } enable_debconf() if $opt_debconf; if (defined $opt_user) { my ($user, $group) = $opt_user =~ m/^([^\s:]++)(?::(\S+))?$/ or error('invalid user/group specification'); if ($user =~ m/^\d+$/) { (undef, undef, $ldd_uid, $ldd_gid) = getpwuid($user) or error("$user: no such user"); } else { (undef, undef, $ldd_uid, $ldd_gid) = getpwnam($user) or error("$user: no such user"); } if (defined $group) { if ($group =~ m/^\d+$/) { (undef, undef, $ldd_gid) = getgrgid($group) or error("$group: no such group"); } else { (undef, undef, $ldd_gid) = getgrnam($group) or error("$group: no such group"); } } } if ($opt_apt_preinst) { error('--apt-preinst and --pending cannot be used together') if $opt_pending; error('--apt-preinst and --all cannot be used together') if $opt_all; error('--apt-preinst and --root cannot be used together') if defined $opt_root; error('too many arguments') if @ARGV; read_pending(); do_apt_preinst(); } elsif ($opt_pending) { error('--pending and --all cannot be used together') if $opt_all; error('--pending and --root cannot be used together') if defined $opt_root; error('too many arguments') if (@ARGV); read_pending(); do_pending(); } else { error('too many arguments') if ($opt_all and @ARGV); error('no packages to check') if (not $opt_all and not @ARGV); if (defined $opt_root) { chroot($opt_root) or die "chroot $opt_root: $!"; chdir('/') or die "chdir /: $!"; } process(0, @ARGV); } exit(0); END { # Catch late write errors: local $! = 0; close(STDOUT) or die $!; close(STDERR) or die $!; } # vim:ts=4 sw=4 et adequate-0.9.1ubuntu1/doc/0000775000000000000000000000000012253576572012311 5ustar adequate-0.9.1ubuntu1/doc/Makefile0000664000000000000000000000063012250050227013725 0ustar version = $(word 2,$(shell cd .. && dpkg-parsechangelog | grep ^Version:)) .PHONY: all all: adequate.1 tags.desc: ../adequate ../private/update-tags adequate.pod: adequate.podt tags.desc ../adequate ../private/generate-manpage > $(@).tmp mv $(@).tmp $(@) %.1: %.pod pod2man --utf8 -c '' -q none -r "$(*) $(version)" $(<) $(@) .PHONY: clean clean: rm -f adequate.pod *.1 *.tmp # vim:ts=4 sw=4 noet adequate-0.9.1ubuntu1/doc/todo.perlcritic0000664000000000000000000000176412253576331015341 0ustar 1 files. 31 subroutines/methods. 950 statements. 1,020 lines, consisting of: 50 blank lines. 66 comment lines. 0 data lines. 904 lines of Perl code. 0 lines of POD. Average McCabe score of subroutines was 8.23. 31 violations. Violations per file was 31.000. Violations per statement was 0.033. Violations per line of code was 0.030. 5 severity 4 violations. 9 severity 3 violations. 15 severity 2 violations. 2 severity 1 violations. 1 violations of CodeLayout::RequireTidyCode. 1 violations of CodeLayout::RequireTrailingCommas. 14 violations of ControlStructures::ProhibitPostfixControls. 2 violations of InputOutput::ProhibitReadlineInForLoop. 3 violations of InputOutput::RequireBriefOpen. 1 violations of Miscellanea::ProhibitUselessNoCritic. 1 violations of Modules::ProhibitExcessMainComplexity. 5 violations of RegularExpressions::ProhibitComplexRegexes. 2 violations of Subroutines::ProhibitExcessComplexity. 1 violations of Variables::ProhibitReusedNames. adequate-0.9.1ubuntu1/doc/tags.desc0000664000000000000000000000526612253576440014112 0ustar Tag: bin-or-sbin-binary-requires-usr-lib-library Description: This package ships a binary in /bin or /sbin that requires a library in /usr/lib. This will make impossible to use this binary before /usr is mounted. Tag: broken-symlink Description: This package ships a symlink which points to a non-existent file. Tag: incompatible-licenses Description: Licenses of the libraries the binary is linked to are incompatible. Tag: library-not-found Description: The binary is linked with a library, which cannot be found. References: Debian Policy §8.6 Tag: missing-alternative Description: This package is a provider of the virtual package `x-terminal-emulator`, but it doesn't register itself as an alternative for `/usr/bin/x-terminal-emulator`; or it is a provider of the virtual package `x-window-manager`, but it doesn't register itself as an alternative for `/usr/bin/x-window-manager`. References: Debian Policy §11.8.3 Debian Policy §11.8.4 Tag: missing-copyright-file Description: The copyright file for this package is missing. This often happens if /usr/share/doc// was a real directory in a previous version of the package, but it's now a symlink; dpkg never replaces directory with a symlink to a directory. References: Debian Policy §12.5, §6.6 Tag: missing-symbol-version-information Description: The binary uses versioned symbols, but the library provides only unversioned ones. Tag: obsolete-conffile Description: The current version of this package no longer ships a conffile (that used to be included in the past). However, the conffile hasn't been removed on upgrade. References: http://wiki.debian.org/DpkgConffileHandling dpkg-maintscript-helper(1) Tag: program-name-collision Description: This package ships a program with the same name as another program. References: Debian Policy §10.1 Tag: py-file-not-bytecompiled Description: This package ships Python modules that are not byte-compiled. References: Python Policy §2.6 Tag: pyshared-file-not-bytecompiled Description: This package ships Python modules in /usr/share/pyshared that are not byte-compiled. References: Python Policy §2.6, §1.5 Tag: symbol-size-mismatch Description: The symbol has changed size since the package was built. It might be an indication that the library broke ABI. If ABI wasn't broken, and the library bumped shlibs (or symbols), the package should be binNMUed. Tag: undefined-symbol Description: The symbol has not been found in the libraries linked with the binary. Either the binary either needs to be linked with an additional shared library, or the dependency on the shared library package that provides this symbol is too weak. References: Debian Policy §3.5, §8.6, §10.2 adequate-0.9.1ubuntu1/doc/adequate.podt0000664000000000000000000000500512250050227014747 0ustar =encoding UTF-8 =head1 NAME adequate - Debian package quality testing tool =head1 SYNOPSIS =over 1 =item B [I] I... =item B [I] --all =item B [I] --apt-preinst =item B [I] --pending =item B --help =back =head1 DESCRIPTION B checks packages installed on the system and reports bugs and policy violations. =head1 OPTIONS =over 4 =item B<--all> Run checks against all the installed packages. =item B<--tags> I[,I...] Emit only these tags. =item B<--tags> -I[,I...] Don't emit these tags. =item B<--debconf> Report issues via L. =item B<--root> I Change the root directory (using L). =item B<--user> I[:I] Switch user and group before running any checks. This is most useful together with B<--root> or B<--pending>, which require superuser privileges. =item B<--apt-preinst> Read APT configuration and F<.deb> filenames from stdin, and append packages names to F for later processing (see B<--pending>). This option is used internally by the APT hook. The hook is disabled by default; please edit F to enable it. =item B<--pending> Run checks against packages listed in F, then empty the file. =item B<--help> Display help and exit. =back =head1 TAGS =head1 EXAMPLES =over 4 =item B> Check the B package. =item B> Check all the packages for obsolete conffiles. =item B> Check all the packages, ignoring Python bytecompilation issues. =back =head1 REPORTING BUGS If you report a bug that was found by adequate, please use the following usertags: =over 4 =item Z<> =over =item User: debian-qa@lists.debian.org =item Usertags: adequate I =back =back Please keep in mind that adequate is not perfect; therefore false positives are possible. Don't report the bug unless you understand the underlying problem. When in doubt, please ask at I first. =head1 SEE ALSO Debian Policy: F or L Python Policy: F or L L, L =for comment vim:ft=pod adequate-0.9.1ubuntu1/templates/0000775000000000000000000000000012124052225013517 5ustar adequate-0.9.1ubuntu1/templates/adequate.templates0000664000000000000000000000007112006551124017227 0ustar Template: adequate/error Type: note Description: ${tags} adequate-0.9.1ubuntu1/apt.conf.d/0000775000000000000000000000000012117712467013470 5ustar adequate-0.9.1ubuntu1/apt.conf.d/20adequate0000664000000000000000000000064412117712467015352 0ustar // If you set this to "true", adequate will run on every install, reporting the // results via debconf. Adequate::Enabled "false"; DPkg::Pre-Install-Pkgs { "adequate --help >/dev/null 2>&1 || exit 0; exec adequate --user nobody --apt-preinst"; }; DPkg::Post-Invoke { "adequate --help >/dev/null 2>&1 || exit 0; exec adequate --debconf --user nobody --pending"; }; DPkg::Tools::Options::adequate::Version "2"; adequate-0.9.1ubuntu1/.perlcriticrc0000664000000000000000000000315712253556054014231 0ustar severity = 1 verbose = 1 [-InputOutput::ProhibitInteractiveTest] # we don't want to depend on IO::Interactive, and -t is good enough for our purposes [-InputOutput::ProhibitExplicitStdin] # we never want to iterate over @ARGV implicitly # see also https://github.com/Perl-Critic/Perl-Critic/issues/72 [InputOutput::RequireBriefOpen] lines = 20 [-InputOutput::RequireCheckedOpen] [-InputOutput::RequireCheckedClose] # already covered by RequireCheckedSyscalls [-RegularExpressions::RequireExtendedFormatting] # no, thanks [-RegularExpressions::RequireDotMatchAnything] [-RegularExpressions::RequireLineBoundaryMatching] # we work mostly with single-line strings # reconsider when https://github.com/Perl-Critic/Perl-Critic/issues/551 is fixed [-RegularExpressions::ProhibitEnumeratedClasses] # we work mostly with ASCII [-ErrorHandling::RequireCarping] # die is good enough for our purposes [-Variables::ProhibitPackageVars] # no, thanks [Variables::ProhibitPunctuationVars] allow = $@ $! $? [-ValuesAndExpressions::ProhibitVersionStrings] # we don't care about Perl << 5.6, which doesn't support version strings [-ValuesAndExpressions::ProhibitMagicNumbers] # no, thanks [-ValuesAndExpressions::ProhibitNoisyQuotes] # no, thanks [-Modules::RequireVersionVar] # useful only for modules [-CodeLayout::ProhibitParensWithBuiltins] # no, thanks [-Miscellanea::RequireRcsKeywords] # no, thanks [-BuiltinFunctions::ProhibitBooleanGrep] # we don't want to depend on List::MoreUtils [NamingConventions::Capitalization] labels = :all_lower file_lexical_variable_exemptions = ARGV_[[:lower:]]+ subroutine_exemptions = Tags # vim:ft=dosini