diff --git a/debian/apport-autoreport.path b/debian/apport-autoreport.path new file mode 100644 index 0000000..5217ba7 --- /dev/null +++ b/debian/apport-autoreport.path @@ -0,0 +1,9 @@ +[Unit] +Description=Process error reports when automatic reporting is enabled (file watch) +ConditionPathExists=/var/lib/apport/autoreport + +[Path] +PathChanged=/var/crash + +[Install] +WantedBy=paths.target diff --git a/debian/apport-autoreport.service b/debian/apport-autoreport.service new file mode 100644 index 0000000..215d06f --- /dev/null +++ b/debian/apport-autoreport.service @@ -0,0 +1,9 @@ +[Unit] +Description=Process error reports when automatic reporting is enabled +ConditionPathExists=/var/lib/apport/autoreport +Wants=whoopsie.path +After=whoopsie.path + +[Service] +Type=oneshot +ExecStart=/usr/share/apport/whoopsie-upload-all --timeout 20 diff --git a/debian/apport-autoreport.timer b/debian/apport-autoreport.timer new file mode 100644 index 0000000..de7257a --- /dev/null +++ b/debian/apport-autoreport.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Process error reports when automatic reporting is enabled (timer based) +ConditionPathExists=/var/lib/apport/autoreport + +[Timer] +OnStartupSec=1h +OnUnitActiveSec=3h + +[Install] +WantedBy=timers.target diff --git a/debian/apport-core-dump-handler.install b/debian/apport-core-dump-handler.install new file mode 100644 index 0000000..124c9fd --- /dev/null +++ b/debian/apport-core-dump-handler.install @@ -0,0 +1,2 @@ +debian/tmp/etc/init.d/apport +usr/lib/systemd/system/apport.service diff --git a/debian/apport-gtk.install b/debian/apport-gtk.install new file mode 100644 index 0000000..ff0900b --- /dev/null +++ b/debian/apport-gtk.install @@ -0,0 +1,2 @@ +usr/share/applications/*gtk* +usr/share/apport/*gtk* diff --git a/debian/apport-kde.install b/debian/apport-kde.install new file mode 100644 index 0000000..76a1057 --- /dev/null +++ b/debian/apport-kde.install @@ -0,0 +1,10 @@ +usr/share/applications/apport-kde-mime.desktop +usr/share/applications/apport-kde-mimelnk.desktop usr/share/mimelnk/text +usr/share/applications/apport-kde.desktop +usr/share/apport/*kde* +usr/share/apport/bugreport.ui +usr/share/apport/choices.ui +usr/share/apport/error.ui +usr/share/apport/progress.ui +usr/share/apport/spinner.gif +usr/share/apport/userpass.ui diff --git a/debian/apport-noui.dirs b/debian/apport-noui.dirs new file mode 100644 index 0000000..4686121 --- /dev/null +++ b/debian/apport-noui.dirs @@ -0,0 +1 @@ +/var/lib/apport diff --git a/debian/apport-noui.postinst b/debian/apport-noui.postinst new file mode 100644 index 0000000..a8dbe6a --- /dev/null +++ b/debian/apport-noui.postinst @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +touch /var/lib/apport/autoreport + +# start the autoreport service on installation +if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-invoke start 'apport-autoreport.path' >/dev/null || true +fi + +#DEBHELPER# diff --git a/debian/apport-noui.prerm b/debian/apport-noui.prerm new file mode 100644 index 0000000..9f3d895 --- /dev/null +++ b/debian/apport-noui.prerm @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +if [ "$1" = remove ]; then + rm -f /var/lib/apport/autoreport +fi + +# Transition the units to apport package itself +if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-invoke stop 'apport-noui.path' 'apport-noui.service' >/dev/null || true + deb-systemd-helper purge 'apport-noui.path' >/dev/null || true + deb-systemd-helper unmask 'apport-noui.path' >/dev/null || true + deb-systemd-invoke stop 'apport-autoreport.path' >/dev/null || true +fi + +#DEBHELPER# diff --git a/debian/apport-retrace.install b/debian/apport-retrace.install new file mode 100644 index 0000000..6b27380 --- /dev/null +++ b/debian/apport-retrace.install @@ -0,0 +1,5 @@ +usr/bin/apport-retrace +usr/bin/crash-digger +usr/bin/dupdb-admin +usr/share/man/man1/apport-retrace.1 +usr/share/man/man1/dupdb-admin.1 diff --git a/debian/apport-valgrind.install b/debian/apport-valgrind.install new file mode 100644 index 0000000..448e695 --- /dev/null +++ b/debian/apport-valgrind.install @@ -0,0 +1,2 @@ +usr/bin/apport-valgrind +usr/share/man/man1/apport-valgrind.1 diff --git a/debian/apport.install b/debian/apport.install new file mode 100644 index 0000000..d80ca39 --- /dev/null +++ b/debian/apport.install @@ -0,0 +1,46 @@ +../../java/testsuite/crash.class usr/share/apport/testsuite/ +../../java/testsuite/crash.jar usr/share/apport/testsuite/ +debian/apport-autoreport.path /lib/systemd/system +debian/apport-autoreport.service /lib/systemd/system +debian/apport-autoreport.timer /lib/systemd/system +debian/package-hooks usr/share/apport +debian/tmp/etc/apport +debian/tmp/etc/cron.daily +debian/tmp/etc/default +lib/udev/ +usr/bin/apport-bug +usr/bin/apport-cli +usr/bin/apport-collect +usr/bin/apport-unpack +usr/bin/oem-getlogs +usr/lib/pm-utils +usr/lib/systemd/system/apport-coredump-hook@.service +usr/lib/systemd/system/apport-forward* +usr/lib/systemd/system/systemd-coredump@.service.d +usr/lib/tmpfiles.d/ +usr/share/apport/apport +usr/share/apport/apport-checkreports +usr/share/apport/apportcheckresume +usr/share/apport/dump_acpi_tables.py +usr/share/apport/gcc_ice_hook +usr/share/apport/general-hooks +usr/share/apport/is-enabled +usr/share/apport/iwlwifi_error_dump +usr/share/apport/java_uncaught_exception +usr/share/apport/kernel_crashdump +usr/share/apport/kernel_oops +usr/share/apport/package-hooks +usr/share/apport/package_hook +usr/share/apport/recoverable_problem +usr/share/apport/root_info_wrapper +usr/share/apport/unkillable_shutdown +usr/share/apport/whoopsie-upload-all +usr/share/bash-completion/completions +usr/share/doc/apport +usr/share/icons +usr/share/java/apport.jar usr/share/apport/ +usr/share/locale +usr/share/man/man1/apport-bug.1 +usr/share/man/man1/apport-cli.1 +usr/share/man/man1/apport-unpack.1 +usr/share/polkit-1 diff --git a/debian/apport.links b/debian/apport.links new file mode 100644 index 0000000..8fda9f3 --- /dev/null +++ b/debian/apport.links @@ -0,0 +1,14 @@ +/usr/bin/apport-bug /usr/bin/ubuntu-bug +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-firmware.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-meta-oem-osp1.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-meta-oem.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-meta-raspi.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-meta.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-oem-osp1.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-oem.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-signed-oem-osp1.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-signed-oem.py +/usr/share/apport/package-hooks/source_linux.py /usr/share/apport/package-hooks/source_linux-signed.py +/usr/share/bash-completion/completions/apport-bug /usr/share/bash-completion/completions/ubuntu-bug +/usr/share/man/man1/apport-bug.1.gz /usr/share/man/man1/apport-collect.1.gz +/usr/share/man/man1/apport-bug.1.gz /usr/share/man/man1/ubuntu-bug.1.gz diff --git a/debian/apport.logrotate b/debian/apport.logrotate new file mode 100644 index 0000000..e255fea --- /dev/null +++ b/debian/apport.logrotate @@ -0,0 +1,9 @@ +/var/log/apport.log { + daily + rotate 7 + delaycompress + compress + notifempty + missingok +} + diff --git a/debian/apport.maintscript b/debian/apport.maintscript new file mode 100644 index 0000000..c07bba4 --- /dev/null +++ b/debian/apport.maintscript @@ -0,0 +1,4 @@ +rm_conffile /etc/apport/blacklist.d/README.blacklist 2.25.0 +rm_conffile /etc/apport/blacklist.d/apport 2.25.0 +rm_conffile /etc/apport/native-origins.d/lts-q-backports 2.5.1-0ubuntu8~ +rm_conffile /etc/init/apport.conf 2.20.6-0ubuntu5~ diff --git a/debian/apport.postinst b/debian/apport.postinst new file mode 100644 index 0000000..3e98058 --- /dev/null +++ b/debian/apport.postinst @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +if [ "$1" = configure ]; then + # directory is required for package failures even if apport is disabled and + # thus the upstart job does not run + mkdir -p -m 1777 /var/crash +fi + +#DEBHELPER# diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6d58aa8 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +apport (2.28.1-ok1) nile; urgency=medium + + * Build for openKylin. + + -- Luoyaoming Tue, 30 Apr 2024 16:29:39 +0800 diff --git a/debian/clean b/debian/clean new file mode 100644 index 0000000..3ec0e7f --- /dev/null +++ b/debian/clean @@ -0,0 +1,2 @@ +debhelper/dh_apport.1 +apport/packaging_impl.py diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..65c807c --- /dev/null +++ b/debian/control @@ -0,0 +1,233 @@ +Source: apport +Section: utils +Priority: optional +Build-Depends: + debhelper-compat (= 13), + dh-translations, + gdb, + gir1.2-glib-2.0 (>= 1.29.17), + pkgconf, + python3-all, + python3-gi, +Build-Depends-Indep: + default-jdk-headless | java-sdk-headless, + dh-python, + gir1.2-gtk-3.0 (>= 3.1.90), + gir1.2-wnck-3.0, + intltool, + iputils-ping, + kmod, + pylint, + python3-apt (>= 0.7.9), + python3-dbus, + python3-distutils-extra (>= 2.24~), + python3-launchpadlib, + python3-psutil, + python3-pytest, + python3-requests, + python3-rpm, + python3-systemd, + python3-yaml, + python3-zstandard, + valgrind, +Maintainer: openKylin Developers +Rules-Requires-Root: no +Standards-Version: 4.6.2 +Vcs-Browser: https://code.launchpad.net/~ubuntu-core-dev/ubuntu/+source/apport/+git/apport +Vcs-Git: https://git.launchpad.net/~ubuntu-core-dev/ubuntu/+source/apport +Homepage: https://wiki.ubuntu.com/Apport + +Package: apport +Architecture: all +Depends: + apport-core-dump-handler | python3-zstandard, + apport-core-dump-handler | systemd-coredump, + gir1.2-glib-2.0 (>= 1.29.17), + python3, + python3-apport (>= ${source:Version}), + python3-gi, + sensible-utils, + ${misc:Depends}, +Recommends: apport-symptoms, python3-systemd +Suggests: apport-gtk | apport-kde, pkexec, polkitd +Replaces: python-apport (<< 2.2-0ubuntu1) +Breaks: python-apport (<< 2.2-0ubuntu1) +Pre-Depends: ${misc:Pre-Depends} +Description: automatically generate crash reports for debugging + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + This package also provides a command line frontend for browsing and + handling the crash reports. For desktops, you should consider + installing the GTK+ or Qt user interface (apport-gtk or apport-kde). + +Package: python3-problem-report +Section: python +Architecture: all +Depends: ${misc:Depends}, ${python3:Depends} +Suggests: python3-zstandard +Description: Python 3 library to handle problem reports + This Python library provides an interface for creating, modifying, + and accessing standardized problem reports for program and kernel + crashes and packaging bugs. + . + These problem reports use standard Debian control format syntax + (RFC822). + +Package: python3-apport +Section: python +Architecture: all +Depends: + python3-apt (>= 0.7.9), + python3-httplib2, + python3-launchpadlib, + python3-problem-report (>= 0.94), + python3-requests, + python3-yaml, + ${misc:Depends}, + ${python3:Depends}, +Recommends: apport +Breaks: apport (<< 2.23.1), apport-gtk (<< 2.23.1), apport-kde (<< 2.23.1) +Description: Python 3 library for Apport crash report handling + This Python package provides high-level functions for creating and + handling apport crash reports: + . + * Query available and new reports. + * Add OS, packaging, and process runtime information to a report. + * Various frontend utility functions. + * Python hook to generate crash reports when Python scripts fail. + +Package: apport-core-dump-handler +Architecture: all +Depends: apport (>= 2.27.0-0ubuntu7~), ${misc:Depends} +Breaks: apport (<< 2.27.0-0ubuntu7~) +Replaces: apport (<< 2.27.0-0ubuntu7~), core-dump-handler +Conflicts: core-dump-handler +Provides: core-dump-handler +Pre-Depends: ${misc:Pre-Depends} +Description: Kernel core dump handler for Apport + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + This package provides the service that configures the kernel (via + /proc/sys/kernel/core_pattern) to forward the crash dumps to apport. + +Package: apport-retrace +Section: devel +Architecture: all +Depends: + apt, + binutils, + dpkg-dev, + gdb, + libc6-dbg | libc6-dbgsym | libc-dbg, + python3, + python3-apport (>= ${source:Version}), + python3-launchpadlib, + ${misc:Depends}, +Suggests: gdb-multiarch +Description: tools for reprocessing Apport crash reports + apport-retrace recombines an Apport crash report (either a file or a + Launchpad bug) and debug symbol packages (.ddebs) into fully symbolic + stack traces. This can optionally use a sandbox for installing debug symbol + packages and doing the processing, so that entire process of retracing crashes + can happen with normal user privileges without changing the system. + . + You need to install gdb-multiarch if you want to be able to retrace crash + reports which happened on a different architecture than the one you run + apport-retrace on. + +Package: apport-valgrind +Section: devel +Architecture: all +Depends: + apt, + binutils, + dpkg-dev, + libc6-dbg | libc6-dbgsym | libc-dbg, + python3, + python3-apport (>= ${source:Version}), + valgrind (>= 3.8.1-1ubuntu1~), + ${misc:Depends}, +Description: valgrind wrapper that first downloads debug symbols + apport-valgrind is a valgrind wrapper that automatically downloads related + available debug symbols and provides them to valgrind's memcheck tool, which + is executed. The output is a valgrind log file ("valgrind.log") that contains + stack traces (with as many symbols resolved as available) and that shows + memory leaks. + +Package: apport-gtk +Section: gnome +Architecture: all +Depends: + apport (>= 0.41), + gir1.2-gtk-3.0 (>= 3.1.90), + gir1.2-wnck-3.0, + gnome-terminal | x-terminal-emulator, + procps, + python3, + python3-apport (>= ${source:Version}), + python3-gi, + whoopsie-preferences, + ${misc:Depends}, +Recommends: gdb | gdb-minimal, update-notifier +Description: GTK+ frontend for the apport crash report system + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + This package provides a GTK+ frontend for browsing and handling the + crash reports. + +Package: apport-kde +Section: kde +Architecture: all +Depends: + apport (>= 0.41), + konsole | x-terminal-emulator, + procps, + python3, + python3-apport (>= ${source:Version}), + python3-pyqt5, + ${misc:Depends}, +Recommends: gdb-minimal | gdb, kubuntu-notification-helper | lxqt-notificationd +Description: KDE frontend for the apport crash report system + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + This package provides a KDE frontend for browsing and handling the + crash reports. + +Package: dh-apport +Section: devel +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${perl:Depends} +Description: debhelper extension for the apport crash report system + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + This package provides a debhelper extension to make it easier for other + packages to include apport hooks. + +Package: apport-noui +Architecture: all +Depends: + apport (>= 2.20.10-0ubuntu4), + gdb-minimal | gdb, + procps, + python3, + python3-apport (>= ${source:Version}), + whoopsie (>= 0.2.77), + ${misc:Depends}, +Description: tools for automatically reporting Apport crash reports + apport automatically collects data from crashed processes and + compiles a problem report in /var/crash/. This utilizes the crashdump + helper hook provided by the Ubuntu kernel. + . + Installing this package will configure your system to automatically submit + all new Apport crash reports. Besides that it is an empty package. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..83ed711 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,15 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Contact: Benjamin Drung +Source: https://launchpad.net/apport/+download + +Files: * +Copyright: + Copyright (C) 2006-2022 Canonical Ltd. +License: GPL-2+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-2'. diff --git a/debian/dh-apport.install b/debian/dh-apport.install new file mode 100644 index 0000000..a6ad7c2 --- /dev/null +++ b/debian/dh-apport.install @@ -0,0 +1,2 @@ +../../debhelper/apport.pm usr/share/perl5/Debian/Debhelper/Sequence +../../debhelper/dh_apport usr/bin diff --git a/debian/dh-apport.manpages b/debian/dh-apport.manpages new file mode 100644 index 0000000..a15ac24 --- /dev/null +++ b/debian/dh-apport.manpages @@ -0,0 +1 @@ +debhelper/dh_apport.1 diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..f96e675 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +debian-branch = ubuntu/devel diff --git a/debian/not-installed b/debian/not-installed new file mode 100644 index 0000000..a3675f0 --- /dev/null +++ b/debian/not-installed @@ -0,0 +1,2 @@ +usr/lib/python*/dist-packages/apport-*.egg-info +usr/lib/python*/dist-packages/__pycache__/* diff --git a/debian/package-hooks/source_linux.py b/debian/package-hooks/source_linux.py new file mode 100644 index 0000000..4579915 --- /dev/null +++ b/debian/package-hooks/source_linux.py @@ -0,0 +1,162 @@ +"""Apport package hook for the Linux kernel. + +(c) 2008 Canonical Ltd. +Contributors: +Matt Zimmerman +Martin Pitt +Brian Murray + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. See http://www.gnu.org/copyleft/gpl.html for +the full text of the license. +""" + +import os.path +import re + +import apport +import apport.hookutils + +SUBMIT_SCRIPT = "/usr/bin/kerneloops-submit" + + +# TODO: Split into smaller functions/methods +# pylint: disable-next=too-many-branches,too-many-locals,too-many-statements +def add_info(report, ui): + """Add information for the Linux kernel.""" + # If running an upstream kernel, instruct reporter to file bug upstream + abi = re.search("-(.*?)-", report["Uname"]) + if abi and (abi.group(1) == "999" or re.search("^0\\d", abi.group(1))): + ui.information( + "It appears you are currently running a mainline kernel. It would" + " be better to report this bug upstream at" + " http://bugzilla.kernel.org/ so that the upstream kernel" + " developers are aware of the issue. If you'd still like to file" + " a bug against the Ubuntu kernel, please boot with an official" + " Ubuntu kernel and re-file." + ) + report["UnreportableReason"] = "The running kernel is not an Ubuntu kernel" + return + + version_signature = report.get("ProcVersionSignature", "") + if not version_signature.startswith("Ubuntu ") and "CrashDB" not in report: + report["UnreportableReason"] = "The running kernel is not an Ubuntu kernel" + return + + # Prevent reports against the linux-meta and linux-signed families, + # redirect to the main package. + for src_pkg in ("linux-meta", "linux-signed"): + if report["SourcePackage"].startswith(src_pkg): + report["SourcePackage"] = report["SourcePackage"].replace( + src_pkg, "linux", 1 + ) + + report.setdefault("Tags", "") + + # Tag up back ported kernel reports for easy identification + if report["SourcePackage"].startswith("linux-lts-"): + report["Tags"] += " qa-kernel-lts-testing" + + apport.hookutils.attach_hardware(report) + apport.hookutils.attach_alsa(report) + apport.hookutils.attach_wifi(report) + apport.hookutils.attach_file(report, "/proc/fb", "ProcFB") + + staging_drivers = re.findall( + "(\\w+): module is from the staging directory", report["CurrentDmesg"] + ) + if staging_drivers: + staging_drivers = list(set(staging_drivers)) + report["StagingDrivers"] = " ".join(staging_drivers) + report["Tags"] += " staging" + # Only if there is an existing title prepend '[STAGING]'. + # Changed to prevent bug titles with just '[STAGING] '. + if report.get("Title"): + report["Title"] = "[STAGING] " + report.get("Title") + + apport.hookutils.attach_file_if_exists( + report, "/etc/initramfs-tools/conf.d/resume", key="HibernationDevice" + ) + + uname_release = os.uname()[2] + lrm_package_name = f"linux-restricted-modules-{uname_release}" + lbm_package_name = f"linux-backports-modules-{uname_release}" + + apport.hookutils.attach_related_packages( + report, [lrm_package_name, lbm_package_name, "linux-firmware"] + ) + + if ( + "Failure" in report + and report["Failure"] == "oops" + and "OopsText" in report + and os.path.exists(SUBMIT_SCRIPT) + ): + # tag kerneloopses with the version of the kerneloops package + apport.hookutils.attach_related_packages(report, ["kerneloops-daemon"]) + oopstext = report["OopsText"] + dupe_sig1 = None + dupe_sig2 = None + for line in oopstext.splitlines(): + if line.startswith("BUG:"): + bug = re.compile("at [0-9a-f]+$") + dupe_sig1 = bug.sub("at location", line) + rip = re.compile("^[RE]?IP:") + if re.search(rip, line): + loc = re.compile("\\[<[0-9a-f]+>\\]") + dupe_sig2 = loc.sub("location", line) + if dupe_sig1 and dupe_sig2: + report["DuplicateSignature"] = f"{dupe_sig1} {dupe_sig2}" + # it's from kerneloops, ask the user whether to submit there as well + if ui: + # Some OopsText begin with "--- [ cut here ] ---", so remove it + oopstext = re.sub("---.*\n", "", oopstext) + first_line = re.match(".*\n", oopstext) + instruction_pointer = re.search("(R|E)?IP\\:.*\n", oopstext) + kernel_driver = re.search("(R|E)?IP(:| is at) .*\\[(.*)\\]\n", oopstext) + call_trace = re.search("Call Trace(.*\n){,10}", oopstext) + oops = "" + if first_line: + oops += first_line.group(0) + if instruction_pointer: + oops += instruction_pointer.group(0) + if call_trace: + oops += call_trace.group(0) + if kernel_driver: + report["Tags"] += f" kernel-driver-{kernel_driver.group(3)}" + # 2012-01-13 - disable submission question as kerneloops.org is + # down + # if ui.yesno( + # "This report may also be submitted to http://kerneloops.org/" + # " in order to help collect aggregate information about" + # " kernel problems. This aids in identifying widespread" + # " issues and problematic areas. A condensed summary of" + # " the Oops is shown below. Would you like to submit" + # " information about this crash to kerneloops.org?\n\n%s" + # % oops + # ): + # text = report["OopsText"] + # proc = subprocess.Popen(SUBMIT_SCRIPT, stdin=subprocess.PIPE) + # proc.communicate(text) + elif "Failure" in report and ( + "resume" in report["Failure"] or "suspend" in report["Failure"] + ): + crash_signature = report.crash_signature() + if crash_signature: + report["DuplicateSignature"] = crash_signature + + if report.get("ProblemType") == "Package": + # in case there is a failure with a grub script + apport.hookutils.attach_related_packages(report, ["grub-pc"]) + + +if __name__ == "__main__": + r = apport.Report() + r.add_proc_info() + r.add_os_info() + r["ProcVersionSignature"] = "Ubuntu 3.4.0" + add_info(r, None) + for k, v in r.items(): + print(f"{k}: {v}") diff --git a/debian/package-hooks/source_ubiquity.py b/debian/package-hooks/source_ubiquity.py new file mode 100644 index 0000000..ffba1e8 --- /dev/null +++ b/debian/package-hooks/source_ubiquity.py @@ -0,0 +1,213 @@ +"""Apport package hook for the ubiquity live CD installer. + +Copyright (C) 2009 Canonical Ltd. +Authors: Colin Watson , + Brian Murray +""" # noqa: D208 + +import os.path +import pathlib +import re + +import apport.hookutils + + +def _add_installation_log(report, ident, name): + log_file = False + for try_location in ("/var/log/installer/%s", "/var/log/%s", "/var/log/upstart/%s"): + if os.path.exists(try_location % name): + log_file = try_location % name + break + if not log_file: + return + + if os.access(log_file, os.R_OK): + report[ident] = pathlib.Path(log_file).read_text("UTF-8", "replace") + elif os.path.exists(log_file): + apport.hookutils.attach_root_command_outputs( + report, {ident: f"cat '{log_file}'"} + ) + + if ident in report and isinstance(report[ident], bytes): + try: + report[ident] = report[ident].decode("UTF-8", "replace") + except (UnicodeDecodeError, KeyError): + pass + + +def _prepare_duplicate_signature(syslog, collect_grub, collect_trace): + collect = "" + for line in syslog.split("\n"): + if collect_grub: + if "grub-installer:" in line and collect == "": + collect = " ".join(line.split(" ")[4:]) + "\n" + continue + if "grub-installer:" in line and collect != "": + collect += " ".join(line.split(" ")[4:]) + "\n" + continue + if not collect_trace and collect != "": + return collect + if "Traceback (most recent call last):" in line and collect_grub: + collect += " ".join(line.split(" ")[5:]) + "\n" + continue + if "Traceback (most recent call last):" in line and not collect_grub: + collect = " ".join(line.split(" ")[5:]) + "\n" + continue + if len(line.split(" ")[5:]) == 1 and "Traceback" in collect: + if collect != "": + return collect + if "Traceback" not in collect: + continue + collect += " ".join(line.split(" ")[5:]) + "\n" + return None + + +# TODO: Split into smaller functions/methods +# pylint: disable-next=too-many-branches,too-many-locals,too-many-statements +def add_info(report, ui): + """Add information for ubiquity live CD installer.""" + _add_installation_log(report, "UbiquitySyslog", "syslog") + syslog = report["UbiquitySyslog"] + if "Buffer I/O error on device" in syslog: + if re.search("Attached .* CD-ROM (\\w+)", syslog): + cd_drive = re.search("Attached .* CD-ROM (\\w+)", syslog).group(1) + cd_error = re.search(f"Buffer I/O error on device {cd_drive}", syslog) + else: + cd_error = None + if cd_error: + ui.information( + "The system log from your installation contains an error. The" + " specific error commonly occurs when there is an issue with" + " the media from which you were installing. This can happen" + " when your media is dirty or damaged or when you've burned" + " the media at a high speed. Please try cleaning the media" + " and or burning new media at a lower speed. In the event" + " that you continue to encounter these errors it may be an" + " issue with your CD / DVD drive." + ) + raise StopIteration + if "I/O error, dev" in syslog: + # check for either usb stick (install media) or hard disk I/O errors + if re.search("I/O error, dev (\\w+)", syslog): + error_disk = re.search("I/O error, dev (\\w+)", syslog).group(1) + mount = apport.hookutils.command_output( + ["grep", error_disk, "/proc/mounts"] + ) + if "target" in mount: + ui.information( + "The system log from your installation contains an error." + " The specific error commonly occurs when there is an" + " issue with the disk to which you are trying to install" + " Ubuntu. It is recommended that you back up important" + " data on your disk and investigate the situation." + " Measures you might take include checking cable" + " connections for your disks and using software tools to" + " investigate the health of your hardware." + ) + raise StopIteration + if "cdrom" in mount: + ui.information( + "The system log from your installation contains an error." + " The specific error commonly occurs when there is an" + " issue with the media from which you were installing." + " Please try creating the USB stick you were installing" + " from again or try installing from a different USB stick." + ) + raise StopIteration + if "SQUASHFS error: Unable to read" in syslog: + ui.information( + "The system log from your installation contains an error. The" + " specific error commonly occurs when there is an issue with the" + " media from which you were installing. This can happen when your" + " media is dirty or damaged or when you've burned the media at a" + " high speed. Please try cleaning the media and or burning new" + " media at a lower speed. In the event that you continue to" + " encounter these errors it may be an issue with your CD / DVD" + " drive." + ) + raise StopIteration + + if "Kernel command line" in syslog: + install_cmdline = re.search("Kernel command line: (.*)", syslog).group(1) + else: + install_cmdline = None + if install_cmdline: + report["InstallCmdLine"] = install_cmdline + + if "Traceback" not in report: + collect_grub = False + collect_trace = False + if ( + "grub-install ran successfully" not in syslog + and "grub-installer:" in syslog + ): + collect_grub = True + if "Traceback" in syslog: + collect_trace = True + if ( + report["ProblemType"] != "Bug" + and collect_grub + or report["ProblemType"] != "Bug" + and collect_trace + ): + duplicate_signature = _prepare_duplicate_signature( + syslog, collect_grub, collect_trace + ) + if duplicate_signature: + report["DuplicateSignature"] = duplicate_signature + if collect_grub: + report["SourcePackage"] = "grub-installer" + + match = re.search("ubiquity.*Ubiquity (.*)\n", syslog) + if match: + match = match.group(1) + report.setdefault("Tags", "") + if match: + report["Tags"] += f" ubiquity-{match.split()[0]}" + + # tag bug reports where people choose to "upgrade" their install of Ubuntu + if re.search("UpgradeSystem\\(\\) was called with safe mode", syslog): + report["Tags"] += " ubiquity-upgrade" + + _add_installation_log(report, "UbiquityPartman", "partman") + + debug_log = "/var/log/installer/debug" + debug_mode = False + if os.path.exists(debug_log): + try: + debug_log_fp = open(debug_log, "r", encoding="utf-8") + except (OSError, IOError): + pass + else: + with debug_log_fp: + for line in debug_log_fp: + if line.startswith("debconf (developer)"): + debug_mode = True + break + if debug_mode: + response = ui.yesno( + "The debug log file from your installation would help us" + " a lot but includes the password you used for your user" + " when installing Ubuntu. Do you want to include this" + " log file?" + ) + if response is None: + raise StopIteration + if response: + _add_installation_log(report, "UbiquityDebug", "debug") + else: + _add_installation_log(report, "UbiquityDebug", "debug") + + _add_installation_log(report, "UbiquityDm", "dm") + _add_installation_log(report, "UpstartUbiquity", "ubiquity.log") + + # add seed name as Tag so we know which image was used + cmdline = pathlib.Path("/proc/cmdline").read_text(encoding="utf-8") + match = re.search("([^/]+)\\.seed", cmdline) + if match: + report["Tags"] += " " + match.group(1) + + _add_installation_log(report, "Casper", "casper.log") + _add_installation_log(report, "OemConfigLog", "oem-config.log") + if "OemConfigLog" in report: + report["Tags"] += " oem-config" diff --git a/debian/package-hooks/subiquity.py b/debian/package-hooks/subiquity.py new file mode 100644 index 0000000..9519b1f --- /dev/null +++ b/debian/package-hooks/subiquity.py @@ -0,0 +1,52 @@ +"""Send reports about subiquity to the correct Launchpad project.""" + +import os + +from apport import hookutils + + +def add_info(report, unused_ui): + """Send reports about subiquity to the correct Launchpad project.""" + # TODO: read the version from the log file? + logfile = os.path.realpath("/var/log/installer/subiquity-debug.log") + revision = "unknown" + if os.path.exists(logfile): + hookutils.attach_file(report, "logfile", "InstallerLog") + with open(logfile, encoding="utf-8") as log_fp: + first_line = log_fp.readline() + marker = "Starting Subiquity revision" + if marker in first_line: + revision = first_line.split(marker)[1].strip() + report["Package"] = f"subiquity ({revision})" + report["SourcePackage"] = "subiquity" + # rewrite this section so the report goes to the project in Launchpad + report[ + "CrashDB" + ] = """\ +{ + "impl": "launchpad", + "project": "subiquity", + "bug_pattern_url": "http://people.canonical.com/" + "~ubuntu-archive/bugpatterns/bugpatterns.xml", +} +""" + + # add in subiquity stuff + hookutils.attach_file_if_exists( + report, "/var/log/installer/subiquity-curtin-install.conf", "CurtinConfig" + ) + hookutils.attach_file_if_exists( + report, "/var/log/installer/curtin-install.log", "CurtinLog" + ) + hookutils.attach_file_if_exists( + report, "/var/log/installer/block/probe-data.json", "ProbeData" + ) + + # collect desktop installer details if available + desktoplog = os.path.realpath("/var/log/installer/ubuntu_bootstrap.log") + if os.path.exists(desktoplog): + hookutils.attach_file(report, desktoplog, "DesktopInstallerLog") + report.add_tags(["ubuntu-desktop-bootstrap"]) + snapdir = os.path.realpath("/snap/ubuntu-desktop-bootstrap/current") + if os.path.exists(snapdir): + report["DesktopInstallerRev"] = os.path.basename(snapdir) diff --git a/debian/package-hooks/ubuntu-desktop-bootstrap.py b/debian/package-hooks/ubuntu-desktop-bootstrap.py new file mode 100644 index 0000000..4b39f9c --- /dev/null +++ b/debian/package-hooks/ubuntu-desktop-bootstrap.py @@ -0,0 +1,44 @@ +"""Send reports about ubuntu-desktop-bootstrap to the correct Launchpad +project.""" + +# pylint: disable=invalid-name +# pylint: enable=invalid-name + +import os + +from apport import hookutils + + +def add_info(report, unused_ui): + """Send reports about ubuntu-desktop-bootstrap to the correct Launchpad + project.""" + udblog = os.path.realpath("/var/log/installer/ubuntu_bootstrap.log") + hookutils.attach_file_if_exists(report, udblog, "UdbLog") + + report["SourcePackage"] = "ubuntu-desktop-bootstrap" + # rewrite this section so the report goes to the project in Launchpad + report[ + "CrashDB" + ] = """\ +{ + "impl": "launchpad", + "project": "ubuntu-desktop-bootstrap", + "bug_pattern_url": "http://people.canonical.com/" + "~ubuntu-archive/bugpatterns/bugpatterns.xml", +} +""" + + subiquitylog = os.path.realpath("/var/log/installer/subiquity-server-debug.log") + hookutils.attach_file_if_exists(report, subiquitylog, "SubiquityLog") + + hookutils.attach_file_if_exists( + report, "/var/log/installer/subiquity-curtin-install.conf", "CurtinConfig" + ) + hookutils.attach_file_if_exists(report, "/var/log/curtin/install.log", "CurtinLog") + hookutils.attach_file_if_exists( + report, "/var/log/curtin/curtin-error-logs.tar", "CurtinError" + ) + + hookutils.attach_file_if_exists( + report, "/var/log/installer/block/probe-data.json", "ProbeData" + ) diff --git a/debian/patches/Add-bin-oem-getlogs.patch b/debian/patches/Add-bin-oem-getlogs.patch new file mode 100644 index 0000000..b288703 --- /dev/null +++ b/debian/patches/Add-bin-oem-getlogs.patch @@ -0,0 +1,323 @@ +From: Benjamin Drung +Date: Thu, 9 Jun 2022 15:20:43 +0200 +Subject: Add bin/oem-getlogs + +Forwarded: not-needed +--- + bin/oem-getlogs | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 307 insertions(+) + create mode 100755 bin/oem-getlogs + +diff --git a/bin/oem-getlogs b/bin/oem-getlogs +new file mode 100755 +index 0000000..8e9cd5a +--- /dev/null ++++ b/bin/oem-getlogs +@@ -0,0 +1,307 @@ ++#! /usr/bin/python3 ++ ++"""Get Hardware Enablement related logs""" ++ ++# TODO: Address following pylint complaints ++# pylint: disable=invalid-name,missing-function-docstring ++ ++import gzip ++import os ++import re ++import shutil ++import subprocess ++import sys ++import tempfile ++import time ++import zipfile ++from argparse import ArgumentParser ++from glob import glob ++from io import BytesIO ++ ++import apport ++from apport import hookutils ++from problem_report import CompressedValue ++ ++opt_debug = False ++ ++ ++# Apport helper routines ++def debug(text): ++ if opt_debug: ++ print(f"{text}\n") ++ ++ ++def attach_command_output(report, command_list, key): ++ debug(" ".join(command_list)) ++ log = hookutils.command_output(command_list) ++ if not log or log[:5] == "Error": ++ return ++ report[key] = log ++ ++ ++def attach_pathglob_as_zip(report, pathglob, key, data_filter=None, mode="b"): ++ """Use zip file here because tarfile module in linux can't ++ properly handle file size 0 with content in /sys directory like ++ edid file. zipfile module works fine here. So we use it. ++ ++ mode: ++ a: for ascii mode of data ++ b: for binary mode of data ++ """ ++ filelist = [] ++ for pg in pathglob: ++ for file in glob(pg): ++ filelist.append(file) ++ ++ zipf = BytesIO() ++ with zipfile.ZipFile(zipf, mode="w", compression=zipfile.ZIP_DEFLATED) as zipobj: ++ for f in filelist: ++ if opt_debug: ++ print(key, f) ++ if not os.path.isfile(f): ++ if opt_debug: ++ print(f, "is not a file") ++ continue ++ if mode == "a": ++ with open(f, encoding="ascii") as f_fd: ++ data = f_fd.read() ++ if data_filter is None: ++ zipobj.writestr(f, data) ++ else: ++ zipobj.writestr(f, data_filter(data)) ++ else: ++ zipobj.write(f) ++ cvalue = CompressedValue() ++ cvalue.set_value(zipf.getbuffer()) ++ report[key + ".zip"] = cvalue ++ ++ ++def attach_nvidia_debug_logs(report, keep_locale=False): ++ # check if nvidia-bug-report.sh exists ++ nv_debug_command = "nvidia-bug-report.sh" ++ ++ if shutil.which(nv_debug_command) is None: ++ if opt_debug: ++ print(nv_debug_command, "does not exist.") ++ return ++ ++ env = os.environ.copy() ++ if not keep_locale: ++ env["LC_MESSAGES"] = "C" ++ ++ # output result to temp directory ++ nv_tempdir = tempfile.mkdtemp() ++ nv_debug_file = "nvidia-bug-report" ++ nv_debug_fullfile = os.path.join(nv_tempdir, nv_debug_file) ++ nv_debug_cmd = [nv_debug_command, "--output-file", nv_debug_fullfile] ++ try: ++ subprocess.run( ++ nv_debug_cmd, ++ env=env, ++ check=False, ++ stdout=subprocess.DEVNULL, ++ stderr=subprocess.DEVNULL, ++ ) ++ nv_debug_fullfile_gz = nv_debug_fullfile + ".gz" ++ hookutils.attach_file_if_exists( ++ report, nv_debug_fullfile_gz, "nvidia-bug-report.gz" ++ ) ++ os.unlink(nv_debug_fullfile_gz) ++ os.rmdir(nv_tempdir) ++ except OSError as e: ++ print("Error:", str(e)) ++ print("Fail on cleanup", nv_tempdir, ". Please file a bug for it.") ++ ++ ++def dot(): ++ print(".", end="", flush=True) ++ ++ ++def build_packages(): ++ # related packages ++ packages = ["apt", "grub2"] ++ ++ # display ++ packages.append("xorg") ++ packages.append("gnome-shell") ++ ++ # audio ++ packages.append("alsa-base") ++ ++ # hotkey and hotplugs ++ packages.append("udev") ++ ++ # networking issues ++ packages.append("network-manager") ++ ++ return packages ++ ++ ++def helper_url_credential_filter(string_with_urls): ++ return re.sub(r"://\w+?:\w+?@", "://USER:SECRET@", string_with_urls) ++ ++ ++def add_info(report): ++ # Check if the DCD file is exist in the installer. ++ attach_command_output(report, ["ubuntu-report", "show"], "UbuntuReport") ++ dot() ++ hookutils.attach_file_if_exists(report, "/etc/buildstamp", "BuildStamp") ++ dot() ++ attach_pathglob_as_zip( ++ report, ++ ["/sys/firmware/acpi/tables/*", "/sys/firmware/acpi/tables/*/*"], ++ "acpitables", ++ ) ++ dot() ++ ++ # Basic hardare information ++ hookutils.attach_hardware(report) ++ dot() ++ hookutils.attach_wifi(report) ++ dot() ++ ++ hwe_system_commands = { ++ "lspci--xxxx": ["lspci", "-xxxx"], ++ "lshw.json": ["lshw", "-json", "-numeric"], ++ "dmidecode": ["dmidecode"], ++ "fwupdmgr_get-devices": [ ++ "fwupdmgr", ++ "get-devices", ++ "--show-all-devices", ++ "--no-unreported-check", ++ ], ++ "boltctl-list": ["boltctl", "list"], ++ "mokutil---sb-state": ["mokutil", "--sb-state"], ++ "tlp-stat": ["tlp-stat"], ++ } ++ for name, command_list in hwe_system_commands.items(): ++ attach_command_output(report, command_list, name) ++ dot() ++ ++ # More audio related ++ hookutils.attach_alsa(report) ++ dot() ++ audio_system_commands = { ++ "pactl-list": ["pactl", "list"], ++ "aplay-l": ["aplay", "-l"], ++ "aplay-L": ["aplay", "-L"], ++ "arecord-l": ["arecord", "-l"], ++ "arecord-L": ["arecord", "-L"], ++ } ++ for name, command_list in audio_system_commands.items(): ++ attach_command_output(report, command_list, name) ++ dot() ++ attach_pathglob_as_zip( ++ report, ++ [ ++ "/usr/share/alsa/ucm/*/*", ++ "/usr/share/alsa/ucm2/*", ++ "/usr/share/alsa/ucm2/*/*", ++ "/usr/share/alsa/ucm2/*/*/*", ++ ], ++ "ALSA-UCM", ++ ) ++ dot() ++ ++ # FIXME: should be included in xorg in the future ++ gfx_system_commands = { ++ "glxinfo": ["glxinfo"], ++ "xrandr": ["xrandr"], ++ "xinput": ["xinput"], ++ } ++ for name, command_list in gfx_system_commands.items(): ++ attach_command_output(report, command_list, name) ++ dot() ++ attach_pathglob_as_zip(report, ["/sys/devices/*/*/drm/card?/*/edid"], "EDID") ++ dot() ++ ++ # nvidia-bug-reports.sh ++ attach_nvidia_debug_logs(report) ++ dot() ++ ++ # FIXME: should be included in thermald in the future ++ attach_pathglob_as_zip( ++ report, ++ ["/etc/thermald/*", "/sys/devices/virtual/thermal/*", "/sys/class/thermal/*"], ++ "THERMALD", ++ ) ++ dot() ++ ++ # all kernel and system messages ++ attach_pathglob_as_zip(report, ["/var/log/*", "/var/log/*/*"], "VAR_LOG") ++ dot() ++ ++ # apt configs ++ attach_pathglob_as_zip( ++ report, ++ [ ++ "/etc/apt/apt.conf.d/*", ++ "/etc/apt/sources.list", ++ "/etc/apt/sources.list.d/*.list", ++ "/etc/apt/preferences.d/*", ++ ], ++ "APT_CONFIGS", ++ mode="a", ++ data_filter=helper_url_credential_filter, ++ ) ++ dot() ++ ++ # TODO: debug information for suspend or hibernate ++ ++ # packages installed. ++ attach_command_output(report, ["dpkg", "-l"], "dpkg-l") ++ dot() ++ ++ # FIXME: should be included in bluez in the future ++ attach_command_output(report, ["hciconfig", "-a"], "hciconfig-a") ++ dot() ++ ++ # FIXME: should be included in dkms in the future ++ attach_command_output(report, ["dkms", "status"], "dkms_status") ++ dot() ++ ++ # enable when the feature to include data from package hooks exists. ++ # packages = build_packages() ++ # attach_related_packages(report, packages) ++ ++ ++def main(): ++ parser = ArgumentParser( ++ prog="oem-getlogs", ++ usage="Useage: sudo -E oem-getlogs [-c CASE_ID]", ++ description=__doc__, ++ ) ++ parser.add_argument( ++ "-c", "--case-id", help="optional CASE_ID", dest="cid", default="" ++ ) ++ args = parser.parse_args() ++ ++ # check if we got root permission ++ if os.geteuid() != 0: ++ print("Error: you need to run this program as root") ++ parser.print_help() ++ sys.exit(1) ++ ++ print("Start to collect logs: ", end="", flush=True) ++ # create report ++ report = apport.Report() ++ add_info(report) ++ ++ # generate filename ++ hostname = os.uname()[1] ++ date_time = time.strftime("%Y%m%d%H%M%S%z", time.localtime()) ++ filename_lst = ["oemlogs", hostname] ++ if len(args.cid) > 0: ++ filename_lst.append(args.cid) ++ filename_lst.append(date_time + ".apport.gz") ++ filename = "-".join(filename_lst) ++ ++ with gzip.open(filename, "wb") as f: ++ report.write(f) ++ print("\nSaved log to", filename) ++ print("The owner of the file is root. You might want to") ++ print(" chown [user]:[group]", filename) ++ ++ ++if __name__ == "__main__": ++ main() diff --git a/debian/patches/Add-general-hooks.patch b/debian/patches/Add-general-hooks.patch new file mode 100644 index 0000000..46923bc --- /dev/null +++ b/debian/patches/Add-general-hooks.patch @@ -0,0 +1,944 @@ +From: Benjamin Drung +Date: Sat, 13 Apr 2024 00:43:11 +0200 +Subject: Add general hooks + +Forwarded: not-needed +--- + apport/hookutils.py | 17 + + data/general-hooks/cloud_archive.py | 42 +++ + data/general-hooks/powerpc.py | 122 +++++++ + data/general-hooks/ubuntu-gnome.py | 69 ++++ + data/general-hooks/ubuntu.py | 635 ++++++++++++++++++++++++++++++++++++ + 5 files changed, 885 insertions(+) + create mode 100644 data/general-hooks/cloud_archive.py + create mode 100644 data/general-hooks/powerpc.py + create mode 100644 data/general-hooks/ubuntu-gnome.py + create mode 100644 data/general-hooks/ubuntu.py + +diff --git a/apport/hookutils.py b/apport/hookutils.py +index 76f7740..dc5a33b 100644 +--- a/apport/hookutils.py ++++ b/apport/hookutils.py +@@ -19,6 +19,7 @@ + import base64 + import datetime + import glob ++import json + import os + import re + import select +@@ -1085,6 +1086,22 @@ def attach_default_grub(report, key=None): + report[key] = "".join(filtered) + + ++def attach_casper_md5check(report, location): ++ """Attach the results of the casper md5check of install media.""" ++ result = "unknown" ++ mismatches = [] ++ if os.path.exists(location): ++ attach_root_command_outputs(report, {"CasperMD5json": f"cat '{location}'"}) ++ if "CasperMD5json" in report: ++ check = json.loads(report["CasperMD5json"]) ++ result = check["result"] ++ mismatches = check["checksum_missmatch"] ++ report["CasperMD5CheckResult"] = result ++ if mismatches: ++ report["CasperMD5CheckMismatches"] = " ".join(mismatches) ++ report.pop("CasperMD5json", None) ++ ++ + # backwards compatible API + shared_libraries = apport.fileutils.shared_libraries + links_with_shared_library = apport.fileutils.links_with_shared_library +diff --git a/data/general-hooks/cloud_archive.py b/data/general-hooks/cloud_archive.py +new file mode 100644 +index 0000000..85e76a8 +--- /dev/null ++++ b/data/general-hooks/cloud_archive.py +@@ -0,0 +1,42 @@ ++"""Redirect reports on packages from the Ubuntu Cloud Archive to the ++launchpad cloud-archive project. ++ ++Copyright (C) 2013 Canonical Ltd. ++Author: James Page ++ ++This program is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the ++Free Software Foundation; either version 2 of the License, or (at your ++option) any later version. See http://www.gnu.org/copyleft/gpl.html for ++the full text of the license. ++""" ++ ++from apport import packaging ++ ++ ++def add_info(report, unused_ui): ++ """Redirect reports on packages from the Ubuntu Cloud Archive to the ++ launchpad cloud-archive project.""" ++ package = report.get("Package") ++ if not package: ++ return ++ package = package.split()[0] ++ try: ++ if ( ++ "~cloud" in packaging.get_version(package) ++ and packaging.get_package_origin(package) == "Canonical" ++ ): ++ report[ ++ "CrashDB" ++ ] = """\ ++{ ++ "impl": "launchpad", ++ "project": "cloud-archive", ++ "bug_pattern_url": "http://people.canonical.com/" ++ "~ubuntu-archive/bugpatterns/bugpatterns.xml", ++} ++""" ++ except ValueError as error: ++ if "does not exist" in str(error): ++ return ++ raise error +diff --git a/data/general-hooks/powerpc.py b/data/general-hooks/powerpc.py +new file mode 100644 +index 0000000..031b4d6 +--- /dev/null ++++ b/data/general-hooks/powerpc.py +@@ -0,0 +1,122 @@ ++# This hook collects logs for Power systems and more specific logs for Pseries, ++# PowerNV platforms. ++# ++# Author: Thierry FAUCK ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++"""IBM Power System related information""" ++ ++import os ++import pathlib ++import platform ++import subprocess ++import tempfile ++ ++from apport.hookutils import ( ++ attach_file, ++ attach_file_if_exists, ++ attach_root_command_outputs, ++ command_available, ++ command_output, ++) ++ ++ ++def _add_tar(report, path, key): ++ (tar_fd, tar_file) = tempfile.mkstemp(prefix="apport.", suffix=".tar") ++ os.close(tar_fd) ++ subprocess.call(["tar", "chf", tar_file, path]) ++ if os.path.getsize(tar_file) > 0: ++ report[key] = (tar_file,) ++ # NB, don't cleanup the temp file, it'll get read later by the apport main ++ # code ++ ++ ++# TODO: Split into smaller functions/methods ++# pylint: disable-next=too-many-branches,too-many-statements ++def add_info(report, unused_ui): ++ """Add IBM Power System related information to report.""" ++ arch = platform.machine() ++ if arch not in ["ppc64", "ppc64le"]: ++ return ++ ++ is_kernel = report["ProblemType"].startswith("Kernel") or "linux" in report.get( ++ "Package", "" ++ ) ++ ++ try: ++ contents = pathlib.Path("/proc/cpuinfo").read_text("utf-8") ++ is_pseries = "pSeries" in contents ++ is_power_nv = "PowerNV" in contents ++ is_power_kvm = "emulated by qemu" in contents ++ except IOError: ++ is_pseries = False ++ is_power_nv = False ++ is_power_kvm = False ++ ++ if is_pseries or is_power_nv: ++ if is_kernel: ++ _add_tar(report, "/proc/device-tree/", "DeviceTree.tar") ++ attach_file(report, "/proc/misc", "ProcMisc") ++ attach_file(report, "/proc/locks", "ProcLocks") ++ attach_file(report, "/proc/loadavg", "ProcLoadAvg") ++ attach_file(report, "/proc/swaps", "ProcSwaps") ++ attach_file(report, "/proc/version", "ProcVersion") ++ report["cpu_smt"] = command_output(["ppc64_cpu", "--smt"]) ++ report["cpu_cores"] = command_output(["ppc64_cpu", "--cores-present"]) ++ report["cpu_coreson"] = command_output(["ppc64_cpu", "--cores-on"]) ++ # To be executed as root ++ if is_kernel: ++ attach_root_command_outputs( ++ report, ++ { ++ "cpu_runmode": "ppc64_cpu --run-mode", ++ "cpu_freq": "ppc64_cpu --frequency", ++ "cpu_dscr": "ppc64_cpu --dscr", ++ "nvram": "cat /dev/nvram", ++ }, ++ ) ++ attach_file_if_exists(report, "/var/log/platform") ++ ++ if is_pseries and not is_power_kvm: ++ attach_file(report, "/proc/ppc64/lparcfg", "ProcLparCfg") ++ attach_file(report, "/proc/ppc64/eeh", "ProcEeh") ++ attach_file(report, "/proc/ppc64/systemcfg", "ProcSystemCfg") ++ report["lscfg_vp"] = command_output(["lscfg", "-vp"]) ++ report["lsmcode"] = command_output(["lsmcode", "-A"]) ++ report["bootlist"] = command_output(["bootlist", "-m", "both", "-r"]) ++ report["lparstat"] = command_output(["lparstat", "-i"]) ++ if command_available("lsvpd"): ++ report["lsvpd"] = command_output(["lsvpd", "--debug"]) ++ if command_available("lsvio"): ++ report["lsvio"] = command_output(["lsvio", "-des"]) ++ if command_available("servicelog"): ++ report["servicelog_dump"] = command_output(["servicelog", "--dump"]) ++ if command_available("servicelog_notify"): ++ report["servicelog_list"] = command_output(["servicelog_notify", "--list"]) ++ if command_available("usysattn"): ++ report["usysattn"] = command_output(["usysattn"]) ++ if command_available("usysident"): ++ report["usysident"] = command_output(["usysident"]) ++ if command_available("serv_config"): ++ report["serv_config"] = command_output(["serv_config", "-l"]) ++ ++ if is_power_nv: ++ _add_tar(report, "/proc/ppc64/", "ProcPpc64.tar") ++ attach_file_if_exists(report, "/sys/firmware/opal/msglog") ++ if os.path.exists("/var/log/dump"): ++ report["VarLogDump_list"] = command_output(["ls", "-l", "/var/log/dump"]) ++ if is_kernel: ++ _add_tar(report, "/var/log/opal-elog", "OpalElog.tar") +diff --git a/data/general-hooks/ubuntu-gnome.py b/data/general-hooks/ubuntu-gnome.py +new file mode 100644 +index 0000000..f088500 +--- /dev/null ++++ b/data/general-hooks/ubuntu-gnome.py +@@ -0,0 +1,69 @@ ++"""Bugs and crashes for the Ubuntu GNOME flavour. ++ ++Copyright (C) 2013 Canonical Ltd. ++Author: Martin Pitt ++ ++This program is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the ++Free Software Foundation; either version 2 of the License, or (at your ++option) any later version. See http://www.gnu.org/copyleft/gpl.html for ++the full text of the license. ++""" ++ ++# pylint: disable=invalid-name ++# pylint: enable=invalid-name ++ ++ ++def add_info(report, unused_ui): ++ """Handle bugs and crashes for the Ubuntu GNOME flavour.""" ++ release = report.get("DistroRelease", "") ++ ++ msg = ( ++ "The GNOME3 PPA you are using is no longer supported" ++ " for this Ubuntu release. Please " ++ ) ++ # redirect reports against PPA packages to ubuntu-gnome project ++ if "[origin: LP-PPA-gnome3-team-gnome3" in report.get("Package", ""): ++ report[ ++ "CrashDB" ++ ] = """\ ++{ ++ "impl": "launchpad", ++ "project": "ubuntu-gnome", ++ "bug_pattern_url": "http://people.canonical.com/" ++ "~ubuntu-archive/bugpatterns/bugpatterns.xml", ++ "dupdb_url": "http://phillw.net/ubuntu-gnome/apport_duplicates/", ++} ++""" ++ ++ # using the staging PPA? ++ if "LP-PPA-gnome3-team-gnome3-staging" in report.get("Package", ""): ++ report.setdefault("Tags", "") ++ report["Tags"] += " gnome3-staging" ++ if release in {"Ubuntu 14.04", "Ubuntu 16.04"}: ++ report["UnreportableReason"] = ( ++ f'{msg} run "ppa-purge ppa:gnome3-team/gnome3-staging".' ++ ) ++ ++ # using the next PPA? ++ elif "LP-PPA-gnome3-team-gnome3-next" in report.get("Package", ""): ++ report.setdefault("Tags", "") ++ report["Tags"] += " gnome3-next" ++ if release in {"Ubuntu 14.04", "Ubuntu 16.04"}: ++ report["UnreportableReason"] = ( ++ f'{msg} run "ppa-purge ppa:gnome3-team/gnome3-next".' ++ ) ++ ++ elif release in {"Ubuntu 14.04", "Ubuntu 16.04"}: ++ report["UnreportableReason"] = ( ++ f'{msg} run "ppa-purge ppa:gnome3-team/gnome3".' ++ ) ++ ++ if "[origin: LP-PPA-gnome3-team-gnome3" in report.get("Dependencies", ""): ++ report.setdefault("Tags", "") ++ report["Tags"] += " gnome3-ppa" ++ if ( ++ release in {"Ubuntu 14.04", "Ubuntu 16.04"} ++ and "UnreportableReason" not in report ++ ): ++ report["UnreportableReason"] = f"{msg} use ppa-purge to remove the PPA." +diff --git a/data/general-hooks/ubuntu.py b/data/general-hooks/ubuntu.py +new file mode 100644 +index 0000000..ea85631 +--- /dev/null ++++ b/data/general-hooks/ubuntu.py +@@ -0,0 +1,635 @@ ++"""Attach generally useful information, not specific to any package. ++ ++Copyright (C) 2009 Canonical Ltd. ++Authors: Matt Zimmerman , ++ Brian Murray ++ ++This program is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the ++Free Software Foundation; either version 2 of the License, or (at your ++option) any later version. See http://www.gnu.org/copyleft/gpl.html for ++the full text of the license. ++""" ++ ++import os ++import pathlib ++import platform ++import re ++import subprocess ++import sys ++import time ++from gettext import gettext as _ ++from glob import glob ++ ++import apport.hookutils ++import apport.packaging ++import problem_report ++ ++ ++def add_info(report, ui): ++ # TODO: Split into smaller functions/methods ++ # pylint: disable=invalid-name,too-many-branches,too-many-locals,too-many-statements ++ # pylint: disable=too-many-nested-blocks ++ """Attach generally useful information, not specific to any package.""" ++ _add_release_info(report) ++ _add_kernel_info(report) ++ add_proposed_info(report) ++ ++ # collect a condensed version of /proc/cpuinfo ++ apport.hookutils.attach_file(report, "/proc/cpuinfo", "ProcCpuinfo") ++ short_cpuinfo = [] ++ for item in reversed(report.get("ProcCpuinfo", "").split("\n")): ++ short_cpuinfo.append(item) ++ if item.startswith("processor\t:"): ++ break ++ short_cpuinfo = reversed(short_cpuinfo) ++ report["ProcCpuinfoMinimal"] = "\n".join(short_cpuinfo) ++ report.pop("ProcCpuinfo") ++ ++ hook_errors = [k for k in report.keys() if k.startswith("HookError_")] ++ if hook_errors: ++ report.add_tags(["apport-hook-error"]) ++ ++ # locally installed python versions can cause a multitude of errors ++ if ( ++ report.get("ProblemType") == "Package" ++ or "python" in report.get("InterpreterPath", "") ++ or "python" in report.get("ExecutablePath", "") ++ ): ++ for python in ("python", "python3"): ++ add_python_details(f"{python.title()}Details", python, report) ++ ++ try: ++ report["ApportVersion"] = apport.packaging.get_version("apport") ++ except ValueError: ++ # might happen on local installs ++ pass ++ ++ # We want to know if people have modified apport's crashdb.conf in case ++ # crashes are reported to Launchpad when they shouldn't be e.g. for a ++ # non-development release. ++ apport.hookutils.attach_conffiles(report, "apport", ui=ui) ++ ++ # Should the system have been rebooted? ++ apport.hookutils.attach_file_if_exists( ++ report, "/var/run/reboot-required.pkgs", "RebootRequiredPkgs" ++ ) ++ ++ casper_md5check = "casper-md5check.json" ++ if "LiveMediaBuild" in report: ++ apport.hookutils.attach_casper_md5check(report, f"/run/{casper_md5check}") ++ else: ++ apport.hookutils.attach_casper_md5check( ++ report, f"/var/log/installer/{casper_md5check}" ++ ) ++ ++ if report.get("ProblemType") == "Package": ++ # every error report regarding a package should have package manager ++ # version information ++ apport.hookutils.attach_related_packages(report, ["dpkg", "apt"]) ++ _check_for_disk_error(report) ++ # check to see if the real root on a persistent media is full ++ if "LiveMediaBuild" in report: ++ st = os.statvfs("/cdrom") ++ free_mb = st.f_bavail * st.f_frsize / 1000000 ++ if free_mb < 10: ++ report["UnreportableReason"] = ( ++ f"Your system partition has less than {free_mb} MB" ++ f" of free space available, which leads to problems" ++ f" using applications and installing updates." ++ f" Please free some space." ++ ) ++ ++ _match_error_messages(report) ++ ++ # these attachments will not exist if ProblemType is Bug as the package ++ # hook runs after the general hook ++ for attachment in ("DpkgTerminalLog", "VarLogDistupgradeApttermlog"): ++ if attachment in report: ++ log_file = _get_attachment_contents(report, attachment) ++ untrimmed_dpkg_log = log_file ++ _check_attachment_for_errors(report, attachment) ++ trimmed_log = _get_attachment_contents(report, attachment) ++ trimmed_log = trimmed_log.split("\n") ++ lines = [] ++ for line in untrimmed_dpkg_log.splitlines(): ++ if line not in trimmed_log: ++ lines.append(str(line)) ++ elif line in trimmed_log: ++ trimmed_log.remove(line) ++ dpkg_log_without_error = "\n".join(lines) ++ ++ # crash reports from live system installer often expose target mount ++ for f in ("ExecutablePath", "InterpreterPath"): ++ if f in report and report[f].startswith("/target/"): ++ report[f] = report[f][7:] ++ ++ # Allow filing update-manager bugs with obsolete packages ++ if report.get("Package", "").startswith("update-manager"): ++ os.environ["APPORT_IGNORE_OBSOLETE_PACKAGES"] = "1" ++ ++ # file bugs against OEM project for modified packages ++ if "Package" in report: ++ v = report["Package"].split()[1] ++ oem_project = get_oem_project(report) ++ if oem_project and ("common" in v or oem_project in v): ++ report["CrashDB"] = "canonical-oem" ++ ++ if "Package" in report: ++ package = report["Package"].split()[0] ++ if package: ++ apport.hookutils.attach_conffiles(report, package, ui=ui) ++ ++ # do not file bugs against "upgrade-system" if it is not installed ++ # (LP#404727) ++ if package == "upgrade-system" and "not installed" in report["Package"]: ++ report["UnreportableReason"] = ( ++ "You do not have the upgrade-system package installed." ++ " Please report package upgrade failures against the package" ++ " that failed to install, or against upgrade-manager." ++ ) ++ ++ # build a duplicate signature tag for package reports ++ if report.get("ProblemType") == "Package": ++ if "DpkgTerminalLog" in report: ++ # this was previously trimmed in check_attachment_for_errors ++ termlog = report["DpkgTerminalLog"] ++ elif "VarLogDistupgradeApttermlog" in report: ++ termlog = _get_attachment_contents(report, "VarLogDistupgradeApttermlog") ++ else: ++ termlog = None ++ if termlog: ++ (package, version) = report["Package"].split(None, 1) ++ # for packages that run update-grub include /etc/default/grub ++ UPDATE_BOOT = [ ++ "friendly-recovery", ++ "linux", ++ "memtest86+", ++ "plymouth", ++ "ubuntu-meta", ++ "virtualbox-ose", ++ ] ++ ug_failure = ( ++ r"/etc/kernel/post(inst|rm)\.d/" ++ r"zz-update-grub exited with return code [1-9]+" ++ ) ++ mkconfig_failure = ( ++ r"/usr/sbin/grub-mkconfig.*/etc/default/grub: Syntax error" ++ ) ++ if re.search(ug_failure, termlog) or re.search(mkconfig_failure, termlog): ++ if report["SourcePackage"] in UPDATE_BOOT: ++ apport.hookutils.attach_default_grub(report, "EtcDefaultGrub") ++ dupe_sig = "" ++ dupe_sig_created = False ++ # messages we expect to see from a package manager (LP: #1692127) ++ pkg_mngr_msgs = re.compile( ++ r"^(Authenticating|De-configuring|Examining|Installing" ++ r"|Preparing|Processing triggers|Purging|Removing|Replaced" ++ r"|Replacing|Setting up|Unpacking|Would remove).*\.\.\.\s*$" ++ ) ++ for line in termlog.split("\n"): ++ if pkg_mngr_msgs.search(line): ++ dupe_sig = f"{line}\n" ++ dupe_sig_created = True ++ continue ++ dupe_sig += f"{line}\n" ++ # this doesn't catch 'dpkg-divert: error' LP: #1581399 ++ if "dpkg: error" in dupe_sig and line.startswith(" "): ++ if "trying to overwrite" in line: ++ conflict_pkg = re.search("in package (.*) ", line) ++ if conflict_pkg and not apport.packaging.is_distro_package( ++ conflict_pkg.group(1) ++ ): ++ report["UnreportableReason"] = _( ++ "An Ubuntu package has a file conflict with a " ++ "package that is not a genuine Ubuntu package." ++ ) ++ report.add_tags(["package-conflict"]) ++ if dupe_sig_created: ++ # the duplicate signature should be the first failure ++ report["DuplicateSignature"] = ( ++ f"package:{package}:{version}\n{dupe_sig}" ++ ) ++ break ++ if dupe_sig: ++ if dpkg_log_without_error.find(dupe_sig) != -1: ++ report["UnreportableReason"] = _( ++ "You have already encountered this package" ++ " installation failure." ++ ) ++ ++ ++def _match_error_messages(report): ++ # There are enough of these now that it is probably worth refactoring... ++ # -mdz ++ if report.get("ProblemType") == "Package": ++ if "failed to install/upgrade: corrupted filesystem tarfile" in report.get( ++ "Title", "" ++ ): ++ report["UnreportableReason"] = ( ++ "This failure was caused by a corrupted package download" ++ " or file system corruption." ++ ) ++ ++ if "is already installed and configured" in report.get("ErrorMessage", ""): ++ report["SourcePackage"] = "dpkg" ++ ++ ++def _check_attachment_for_errors(report, attachment): ++ # TODO: Split into smaller functions/methods ++ # pylint: disable=too-many-branches,too-many-statements,too-many-nested-blocks ++ if report.get("ProblemType") == "Package": ++ wrong_grub_msg = _( ++ """\ ++Your system was initially configured with grub version 2, but you have\ ++ removed it from your system in favor of grub 1 without configuring it.\ ++ To ensure your bootloader configuration is updated whenever a new kernel\ ++ is available, open a terminal and run: ++ ++ sudo apt-get install grub-pc ++""" ++ ) ++ ++ trim_dpkg_log(report) ++ log_file = _get_attachment_contents(report, attachment) ++ ++ grub_hook_failure = "DpkgTerminalLog" in report and bool( ++ re.search( ++ r"^Not creating /boot/grub/menu.lst as you wish", ++ report["DpkgTerminalLog"], ++ re.MULTILINE, ++ ) ++ ) ++ ++ if report["Package"] not in ["grub", "grub2"]: ++ # linux-image postinst emits this when update-grub fails ++ # https://wiki.ubuntu.com/KernelTeam/DebuggingUpdateErrors ++ grub_errors = [ ++ r"^User postinst hook script \[.*update-grub\] exited with value", ++ r"^run-parts: /etc/kernel/post(inst|rm).d" ++ r"/zz-update-grub exited with return code [1-9]+", ++ r"^/usr/sbin/grub-probe: error", ++ ] ++ ++ for grub_error in grub_errors: ++ if attachment in report and re.search( ++ grub_error, log_file, re.MULTILINE ++ ): ++ # File these reports on the grub package instead ++ grub_package = apport.packaging.get_file_package( ++ "/usr/sbin/update-grub" ++ ) ++ if ( ++ grub_package is None ++ or grub_package == "grub" ++ and "grub-probe" not in log_file ++ ): ++ report["SourcePackage"] = "grub" ++ if os.path.exists("/boot/grub/grub.cfg") and grub_hook_failure: ++ report["UnreportableReason"] = wrong_grub_msg ++ else: ++ report["SourcePackage"] = "grub2" ++ ++ if report["Package"] != "initramfs-tools": ++ # update-initramfs emits this when it fails, usually invoked ++ # from the linux-image postinst ++ # https://wiki.ubuntu.com/KernelTeam/DebuggingUpdateErrors ++ if attachment in report and re.search( ++ r"^update-initramfs: failed for ", log_file, re.MULTILINE ++ ): ++ # File these reports on the initramfs-tools package instead ++ report["SourcePackage"] = "initramfs-tools" ++ ++ if report["Package"].startswith("linux-image-") and attachment in report: ++ # /etc/kernel/*.d failures from kernel package postinst ++ match = re.search( ++ r"^run-parts: (/etc/kernel/\S+\.d/\S+) exited with return code \d+", ++ log_file, ++ re.MULTILINE, ++ ) ++ if match: ++ path = match.group(1) ++ package = apport.packaging.get_file_package(path) ++ if package: ++ report["SourcePackage"] = package ++ report["ErrorMessage"] = match.group(0) ++ if package == "grub-pc" and grub_hook_failure: ++ report["UnreportableReason"] = wrong_grub_msg ++ else: ++ report["UnreportableReason"] = ( ++ "This failure was caused by a program" ++ " which did not originate from Ubuntu" ++ ) ++ ++ error_message = report.get("ErrorMessage") ++ corrupt_package = ( ++ "This failure was caused by a corrupted package download" ++ " or file system corruption." ++ ) ++ out_of_memory = "This failure was caused by the system running out of memory." ++ ++ if "failed to install/upgrade: corrupted filesystem tarfile" in report.get( ++ "Title", "" ++ ): ++ report["UnreportableReason"] = corrupt_package ++ ++ if "dependency problems - leaving unconfigured" in error_message: ++ report["UnreportableReason"] = ( ++ "This failure is a followup error from a previous" ++ " package install failure." ++ ) ++ ++ if "failed to allocate memory" in error_message: ++ report["UnreportableReason"] = out_of_memory ++ ++ if "cannot access archive" in error_message: ++ report["UnreportableReason"] = corrupt_package ++ ++ if re.search( ++ r"(failed to read|failed in write|short read) on buffer copy", error_message ++ ): ++ report["UnreportableReason"] = corrupt_package ++ ++ if re.search( ++ r"(failed to read|failed to write|failed to seek" ++ r"|unexpected end of file or stream)", ++ error_message, ++ ): ++ report["UnreportableReason"] = corrupt_package ++ ++ if re.search( ++ r"(--fsys-tarfile|dpkg-deb --control) returned error exit status 2", ++ error_message, ++ ): ++ report["UnreportableReason"] = corrupt_package ++ ++ if attachment in report and re.search( ++ r"dpkg-deb: error.*is not a debian format archive", log_file, re.MULTILINE ++ ): ++ report["UnreportableReason"] = corrupt_package ++ ++ if "is already installed and configured" in report.get("ErrorMessage", ""): ++ # there is insufficient information in the data currently gathered ++ # so gather more data ++ report["SourcePackage"] = "dpkg" ++ report["AptdaemonVersion"] = apport.packaging.get_version("aptdaemon") ++ apport.hookutils.attach_file_if_exists( ++ report, "/var/log/dpkg.log", "DpkgLog" ++ ) ++ apport.hookutils.attach_file_if_exists( ++ report, "/var/log/apt/term.log", "AptTermLog" ++ ) ++ # gather filenames in /var/crash to see if there is one for dpkg ++ reports = glob("/var/crash/*") ++ if reports: ++ report["CrashReports"] = apport.hookutils.command_output( ++ ["stat", "-c", "%a:%u:%g:%s:%y:%x:%n"] + reports ++ ) ++ report.add_tags(["already-installed"]) ++ ++ ++def _check_for_disk_error(report): ++ devs_to_check = [] ++ if "Dmesg.txt" not in report and "CurrentDmesg.txt" not in report: ++ return ++ if "Df.txt" not in report: ++ return ++ df_output = report["Df.txt"] ++ device_error = False ++ for line in df_output: ++ line = line.strip("\n") ++ if line.endswith("/") or line.endswith("/usr") or line.endswith("/var"): ++ # without manipulation it'd look like /dev/sda1 ++ device = line.split(" ")[0].strip("0123456789") ++ device = device.replace("/dev/", "") ++ devs_to_check.append(device) ++ dmesg = report.get("CurrentDmesg.txt", report["Dmesg.txt"]) ++ for line in dmesg: ++ line = line.strip("\n") ++ if "I/O error" in line: ++ # no device in this line ++ if "journal commit I/O error" in line: ++ continue ++ for dev in devs_to_check: ++ if re.search(dev, line): ++ error_device = dev ++ device_error = True ++ break ++ if device_error: ++ report["UnreportableReason"] = ( ++ f"This failure was caused by a hardware error on /dev/{error_device}" ++ ) ++ ++ ++def _add_kernel_info(report): ++ # This includes the Ubuntu packaged kernel version ++ apport.hookutils.attach_file_if_exists( ++ report, "/proc/version_signature", "ProcVersionSignature" ++ ) ++ ++ ++def _add_release_info(report): ++ # https://bugs.launchpad.net/bugs/364649 ++ media = "/var/log/installer/media-info" ++ apport.hookutils.attach_file_if_exists(report, media, "InstallationMedia") ++ # Preinstalled Raspberry Pi images include a build date breadcrumb ++ apport.hookutils.attach_file_if_exists(report, "/.disk/info", "ImageMediaBuild") ++ if "ImageMediaBuild" in report: ++ report.add_tags([f"{report['Architecture']}-image"]) ++ try: ++ compatible = pathlib.Path("/proc/device-tree/compatible").read_bytes() ++ is_a_pi = any( ++ vendor == "raspberrypi" ++ for s in compatible.split(b"\0") ++ if s ++ for vendor, model in (s.decode("ascii").split(",", 1),) ++ ) ++ except FileNotFoundError: ++ is_a_pi = False ++ if is_a_pi: ++ report.add_tags(["raspi-image"]) ++ ++ # if we are running from a live system, add the build timestamp ++ apport.hookutils.attach_file_if_exists( ++ report, "/cdrom/.disk/info", "LiveMediaBuild" ++ ) ++ if os.path.exists("/cdrom/.disk/info"): ++ report["CasperVersion"] = apport.packaging.get_version("casper") ++ ++ # https://wiki.ubuntu.com/FoundationsTeam/Specs/OemTrackingId ++ apport.hookutils.attach_file_if_exists( ++ report, "/var/lib/ubuntu_dist_channel", "DistributionChannelDescriptor" ++ ) ++ ++ os_release = platform.freedesktop_os_release() ++ release_codename = os_release.get("VERSION_CODENAME") ++ if release_codename: ++ report.add_tags([release_codename]) ++ ++ if os.path.exists(media): ++ mtime = os.stat(media).st_mtime ++ human_mtime = time.strftime("%Y-%m-%d", time.gmtime(mtime)) ++ delta = time.time() - mtime ++ report["InstallationDate"] = ( ++ f"Installed on {human_mtime} ({round(delta / 86400)} days ago)" ++ ) ++ ++ log = "/var/log/dist-upgrade/main.log" ++ if os.path.exists(log): ++ mtime = os.stat(log).st_mtime ++ human_mtime = time.strftime("%Y-%m-%d", time.gmtime(mtime)) ++ delta = time.time() - mtime ++ ++ # Would be nice if this also showed which release was originally ++ # installed ++ report["UpgradeStatus"] = ( ++ f"Upgraded to {release_codename} on {human_mtime}" ++ f" ({round(delta / 86400)} days ago)" ++ ) ++ else: ++ report["UpgradeStatus"] = "No upgrade log present (probably fresh install)" ++ ++ ++def add_proposed_info(report): ++ """Tag if package comes from -proposed.""" ++ if "Package" not in report: ++ return ++ try: ++ (package, version) = report["Package"].split()[:2] ++ except ValueError: ++ print("WARNING: malformed Package field: " + report["Package"]) ++ return ++ ++ apt_cache = subprocess.run( ++ ["apt-cache", "showpkg", package], ++ check=False, ++ stdout=subprocess.PIPE, ++ text=True, ++ ) ++ if apt_cache.returncode != 0: ++ print(f"WARNING: apt-cache showpkg {package} failed") ++ return ++ ++ found_proposed = False ++ found_updates = False ++ found_security = False ++ for line in apt_cache.stdout.splitlines(): ++ if line.startswith(version + " ("): ++ if "-proposed_" in line: ++ found_proposed = True ++ if "-updates_" in line: ++ found_updates = True ++ if "-security" in line: ++ found_security = True ++ ++ if found_proposed and not found_updates and not found_security: ++ report.add_tags(["package-from-proposed"]) ++ ++ ++def get_oem_project(report): ++ """Determine OEM project name from Distribution Channel Descriptor. ++ ++ Return None if it cannot be determined or does not exist. ++ """ ++ dcd = report.get("DistributionChannelDescriptor", None) ++ if dcd and dcd.startswith("canonical-oem-"): ++ return dcd.split("-")[2] ++ return None ++ ++ ++def trim_dpkg_log(report): ++ """Trim DpkgTerminalLog to the most recent installation session.""" ++ if "DpkgTerminalLog" not in report: ++ return ++ if not report["DpkgTerminalLog"].strip(): ++ report["UnreportableReason"] = "/var/log/apt/term.log does not contain any data" ++ return ++ lines = [] ++ dpkg_log = report["DpkgTerminalLog"] ++ if isinstance(dpkg_log, bytes): ++ trim_re = re.compile(b"^\\(.* ... \\d+ .*\\)$") ++ start_re = re.compile(b"^Log started:") ++ else: ++ trim_re = re.compile("^\\(.* ... \\d+ .*\\)$") ++ start_re = re.compile("^Log started:") ++ for line in dpkg_log.splitlines(): ++ if start_re.match(line) or trim_re.match(line): ++ lines = [] ++ continue ++ lines.append(line) ++ # If trimming the log file fails, return the whole log file. ++ if not lines: ++ return ++ if isinstance(lines[0], str): ++ report["DpkgTerminalLog"] = "\n".join(lines) ++ else: ++ report["DpkgTerminalLog"] = "\n".join( ++ [str(line.decode("UTF-8", "replace")) for line in lines] ++ ) ++ ++ ++def _get_attachment_contents(report, attachment): ++ if isinstance(report[attachment], problem_report.CompressedValue): ++ contents = report[attachment].get_value().decode("UTF-8") ++ else: ++ contents = report[attachment] ++ return contents ++ ++ ++def add_python_details(key, python, report): ++ """Add comma separated details about which python is being used""" ++ python_path = apport.hookutils.command_output(["which", python]) ++ if python_path.startswith("Error: "): ++ report[key] = "N/A" ++ return ++ python_link = apport.hookutils.command_output(["readlink", "-f", python_path]) ++ python_pkg = apport.fileutils.find_file_package(python_path) ++ if python_pkg: ++ python_pkg_version = apport.packaging.get_version(python_pkg) ++ python_version = apport.hookutils.command_output([python_link, "--version"]) ++ data = f"{python_link}, {python_version}" ++ if python_pkg: ++ data += f", {python_pkg}, {python_pkg_version}" ++ else: ++ data += ", unpackaged" ++ report[key] = data ++ ++ ++def main(): # pylint: disable=missing-function-docstring ++ # for testing: update report file given on command line ++ if len(sys.argv) != 2: ++ sys.stderr.write(f"Usage for testing this hook: {sys.argv[0]} \n") ++ sys.exit(1) ++ ++ report_file = sys.argv[1] ++ ++ report = apport.Report() ++ with open(report_file, "rb") as report_fd: ++ report.load(report_fd) ++ report_keys = set(report.keys()) ++ ++ new_report = report.copy() ++ add_info(new_report, None) ++ ++ new_report_keys = set(new_report.keys()) ++ ++ # Show differences ++ # N.B. Some differences will exist if the report file is not from your ++ # system because the hook runs against your local system. ++ changed = 0 ++ for key in sorted(report_keys | new_report_keys): ++ if key in new_report_keys and key not in report_keys: ++ print(f"+{key}: {new_report[key]}") ++ changed += 1 ++ elif key in report_keys and key not in new_report_keys: ++ print(f"-{key}: (deleted)") ++ changed += 1 ++ elif key in report_keys and key in new_report_keys: ++ if report[key] != new_report[key]: ++ print(f"~{key}: (changed)") ++ changed += 1 ++ print(f"{changed} items changed") ++ ++ ++if __name__ == "__main__": ++ main() diff --git a/debian/patches/Default-to-Ubuntu-crash-DB.patch b/debian/patches/Default-to-Ubuntu-crash-DB.patch new file mode 100644 index 0000000..be7a266 --- /dev/null +++ b/debian/patches/Default-to-Ubuntu-crash-DB.patch @@ -0,0 +1,56 @@ +From: Benjamin Drung +Date: Thu, 9 Jun 2022 15:32:50 +0200 +Subject: Default to Ubuntu crash DB + +Forwarded: not-needed +--- + etc/apport/crashdb.conf | 30 +++++++++++++++++------------- + 1 file changed, 17 insertions(+), 13 deletions(-) + +diff --git a/etc/apport/crashdb.conf b/etc/apport/crashdb.conf +index f826b7c..3f64a13 100644 +--- a/etc/apport/crashdb.conf ++++ b/etc/apport/crashdb.conf +@@ -1,6 +1,18 @@ + # map crash database names to CrashDatabase implementations and URLs + +-default = 'debug' ++default = 'ubuntu' ++ ++def get_oem_project(): ++ '''Determine OEM project name from Distribution Channel Descriptor ++ ++ Return None if it cannot be determined or does not exist. ++ ''' ++ try: ++ dcd = open('/var/lib/ubuntu_dist_channel').read() ++ if dcd.startswith('canonical-oem-'): ++ return dcd.split('-')[2] ++ except IOError: ++ return None + + databases = { + 'ubuntu': { +@@ -11,18 +23,10 @@ databases = { + 'escalation_tag': 'bugpattern-needed', + 'escalated_tag': 'bugpattern-written', + }, +- 'fedora': { +- # NOTE this will change Fall '07 when RHT switches to bugzilla 3.x! +- 'impl': 'rhbugzilla', +- 'bug_pattern_url': 'http://qa.fedoraproject.org/apport/bugpatterns.xml', +- 'distro': 'fedora' +- }, +- 'debian': { +- 'impl': 'debian', +- 'distro': 'debian', +- 'smtphost': 'reportbug.debian.org', +- 'recipient': 'submit@bugs.debian.org', +- 'sender': '' ++ 'canonical-oem': { ++ 'impl': 'launchpad', ++ 'bug_pattern_url': 'http://people.canonical.com/~ubuntu-archive/bugpatterns/bugpatterns.xml', ++ 'project': get_oem_project(), + }, + 'snap-github': { + 'impl': 'github', diff --git a/debian/patches/Disable-Launchpad-crash-reports-for-the-24.04-release.patch b/debian/patches/Disable-Launchpad-crash-reports-for-the-24.04-release.patch new file mode 100644 index 0000000..f5d4e00 --- /dev/null +++ b/debian/patches/Disable-Launchpad-crash-reports-for-the-24.04-release.patch @@ -0,0 +1,20 @@ +From: Benjamin Drung +Date: Thu, 18 Apr 2024 14:50:33 +0200 +Subject: Disable Launchpad crash reports for the 24.04 release + +--- + etc/apport/crashdb.conf | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/etc/apport/crashdb.conf b/etc/apport/crashdb.conf +index 3f64a13..3dac679 100644 +--- a/etc/apport/crashdb.conf ++++ b/etc/apport/crashdb.conf +@@ -20,6 +20,7 @@ databases = { + 'bug_pattern_url': 'http://people.canonical.com/~ubuntu-archive/bugpatterns/bugpatterns.xml', + 'dupdb_url': 'http://people.canonical.com/~ubuntu-archive/apport-duplicates', + 'distro': 'ubuntu', ++ 'problem_types': ['Bug', 'Package'], + 'escalation_tag': 'bugpattern-needed', + 'escalated_tag': 'bugpattern-written', + }, diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..6b15464 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,4 @@ +Add-bin-oem-getlogs.patch +Add-general-hooks.patch +Default-to-Ubuntu-crash-DB.patch +Disable-Launchpad-crash-reports-for-the-24.04-release.patch diff --git a/debian/python3-apport.install b/debian/python3-apport.install new file mode 100644 index 0000000..c899d80 --- /dev/null +++ b/debian/python3-apport.install @@ -0,0 +1,2 @@ +usr/lib/python3*/*-packages/apport/* +usr/lib/python3*/*-packages/apport_python_hook.py diff --git a/debian/python3-problem-report.install b/debian/python3-problem-report.install new file mode 100644 index 0000000..6451357 --- /dev/null +++ b/debian/python3-problem-report.install @@ -0,0 +1 @@ +usr/lib/python3*/*-packages/problem_report* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b38b9fd --- /dev/null +++ b/debian/rules @@ -0,0 +1,20 @@ +#!/usr/bin/make -f + +%: + dh "$@" --with python3,translations --buildsystem pybuild + +override_dh_auto_test: +ifeq (, $(findstring nocheck, $(DEB_BUILD_OPTIONS))) + tests/run-linters --errors-only + python3 -m pytest -ra -v tests/unit/ tests/integration/ +endif + +override_dh_installinit: + dh_installinit -papport-core-dump-handler --name=apport --error-handler=true --only-scripts + +override_dh_install: + dh_install -X.pyc -X.egg-info + pod2man -c Debhelper -r "$(DEB_VERSION)" debhelper/dh_apport debhelper/dh_apport.1 + +override_dh_python3: + dh_python3 --skip-private diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/tests/control b/debian/tests/control new file mode 100644 index 0000000..bca4d7b --- /dev/null +++ b/debian/tests/control @@ -0,0 +1,40 @@ +Test-command: cp -r tests "$AUTOPKGTEST_TMP" && cd "$AUTOPKGTEST_TMP" && python3 -m pytest -ra -v tests/unit/ tests/integration/ +Depends: + apport, + apport-retrace, + apport-valgrind, + default-jdk-headless | java-sdk-headless, + dictionaries-common, + gcc, + iputils-ping, + kmod, + libc6-dev, + python3-psutil, + python3-pytest, + python3-systemd, + python3-zstandard, +Restrictions: allow-stderr, needs-root +Features: test-name=unit-and-integration-tests + +Tests: system-tests +Depends: + apport, + apport-gtk, + apport-kde, + apport-retrace, + apport-valgrind, + dbus, + dbus-x11, + dirmngr, + gcc, + gnome-icon-theme, + gpg, + gvfs-daemons, + psmisc, + python3-dbus, + python3-pytest, + python3-systemd, + ubuntu-dbgsym-keyring, + ubuntu-keyring, + xvfb, +Restrictions: allow-stderr, isolation-container, needs-root diff --git a/debian/tests/system-tests b/debian/tests/system-tests new file mode 100755 index 0000000..e3bc6a3 --- /dev/null +++ b/debian/tests/system-tests @@ -0,0 +1,17 @@ +#!/bin/sh +set -eu + +# do not run in the source tree, as we want to check the system-installed +# apport +TESTDIR=${AUTOPKGTEST_TMP:-/tmp} +mkdir -p "$TESTDIR" +cp -r tests "$TESTDIR" +cd "$TESTDIR" + +# clean up old crash reports +rm -rf /var/crash/* + +export GNUPGHOME="$TESTDIR/gnupg" +mkdir -m 700 "$GNUPGHOME" + +xvfb-run dbus-launch python3 -m pytest -ra -v tests/system/ diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..635dd15 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,4 @@ +Bug-Database: https://bugs.launchpad.net/apport +Bug-Submit: https://bugs.launchpad.net/apport/+filebug +Repository: https://github.com/canonical/apport.git +Repository-Browse: https://github.com/canonical/apport diff --git a/debian/upstream/signing-key.asc b/debian/upstream/signing-key.asc new file mode 100644 index 0000000..2a17ce0 --- /dev/null +++ b/debian/upstream/signing-key.asc @@ -0,0 +1,136 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +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 +Nns9QEB40Ql3ynefFsJVMwZ1S9jC +=396c +-----END PGP PUBLIC KEY BLOCK----- diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..4c3d728 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=4 +opts="pgpsigurlmangle=s%$%.asc%" \ + https://launchpad.net/@PACKAGE@/+download .*/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@