From 0e6ac73ce4856b5165c18a6a82be4b508347ffd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Guzm=C3=A1n?= <ark@switnet.org> Date: Wed, 30 Mar 2022 09:44:30 +0000 Subject: [PATCH] Fix update notifier 10.0 --- helpers/DATA/update-notifier/apt_check.py | 285 ++++++++++++++++++++++ helpers/make-update-notifier | 7 +- 2 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 helpers/DATA/update-notifier/apt_check.py diff --git a/helpers/DATA/update-notifier/apt_check.py b/helpers/DATA/update-notifier/apt_check.py new file mode 100644 index 00000000..05334fa4 --- /dev/null +++ b/helpers/DATA/update-notifier/apt_check.py @@ -0,0 +1,285 @@ +#!/usr/bin/python3 + + +# nice apt-get -s -o Debug::NoLocking=true upgrade | grep ^Inst + +import apt +import apt_pkg +import os +import sys +from optparse import OptionParser +import re +import gettext +import distro_info +import subprocess + + +SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences" +OS_RELEASE_PATH = "/etc/os-release" + + +def _get_info_from_os_release(key): + " get info directly from os-release file " + if os.path.exists(OS_RELEASE_PATH): + with open(OS_RELEASE_PATH) as f: + search_res = re.search( + r"{}=(?P<name>.*)".format(key), + f.read() + ) + if search_res: + return search_res.group("name") + else: + raise Exception( + "Could not find {} in {}".format( + key, OS_RELEASE_PATH + ) + ) + else: + raise Exception( + "File {} was not found on the system".format( + OS_RELEASE_PATH + ) + ) + + +def _get_output_from_lsb_release(lsb_option): + " get info from lsb_release output " + return subprocess.check_output( + ["lsb_release", lsb_option, "-s"], universal_newlines=True + ).strip() + + +def get_distro(): + " get distro name " + try: + return _get_info_from_os_release(key="UBUNTU_CODENAME") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-c") + + +DISTRO = get_distro() + +def _(msg): + return gettext.dgettext("update-notifier", msg) + + +def _handleException(type, value, tb): + sys.stderr.write("E: " + _("Unknown Error: '%s' (%s)") % (type, value)) + sys.exit(-1) + + +def get_distro_version(): + " get distro version " + try: + return _get_info_from_os_release(key="VERSION_ID").replace('"', "") + except Exception: + # If the system does not have os-release file or does not have the + # required entry in it, we will get the distro name from lsb_release + # command + return _get_output_from_lsb_release("-r") + + +def clean(cache, depcache): + " unmark (clean) all changes from the given depcache " + # mvo: looping is too inefficient with the new auto-mark code + # for pkg in cache.Packages: + # depcache.MarkKeep(pkg) + depcache.init() + + +def saveDistUpgrade(cache, depcache): + """ this function mimics a upgrade but will never remove anything """ + depcache.upgrade(True) + if depcache.del_count > 0: + clean(cache, depcache) + depcache.upgrade() + + +def isSecurityUpgrade(ver): + " check if the given version is a security update (or masks one) " + security_pockets = [("Ubuntu", "%s-security" % DISTRO), + ("gNewSense", "%s-security" % DISTRO), + ("Debian", "%s-updates" % DISTRO)] + for (file, index) in ver.file_list: + for origin, archive in security_pockets: + if (file.archive == archive and file.origin == origin): + return True + return False + +def write_package_names(outstream, cache, depcache): + " write out package names that change to outstream " + pkgs = [pkg for pkg in cache.packages if depcache.marked_install(pkg) + or depcache.marked_upgrade(pkg)] + outstream.write("\n".join([p.name for p in pkgs])) + + +def is_lts_distro(): + " check if the current distro is LTS or not" + return distro_info.TrisquelDistroInfo().is_lts(DISTRO) + + +def write_human_readable_summary(outstream, upgrades, security_updates): + " write out human summary to outstream " + lts_distro = is_lts_distro() + + outstream.write( + gettext.dngettext("update-notifier", + "%i update can be applied immediately.", + "%i updates can be applied immediately.", + upgrades) % upgrades + ) + + if security_updates > 0: + outstream.write("\n") + outstream.write(gettext.dngettext("update-notifier", + "%i of these updates is a " + "standard security update.", + "%i of these updates are " + "standard security updates.", + security_updates) % + security_updates) + + +def init(): + " init the system, be nice " + # FIXME: do a ionice here too? + os.nice(19) + apt_pkg.init() + + +def run(options=None): + + # we are run in "are security updates installed automatically?" + # question mode + if options.security_updates_unattended: + res = apt_pkg.config.find_i("APT::Periodic::Unattended-Upgrade", 0) + # print(res) + sys.exit(res) + + # get caches + try: + cache = apt_pkg.Cache(apt.progress.base.OpProgress()) + except SystemError as e: + sys.stderr.write("E: " + _("Error: Opening the cache (%s)") % e) + sys.exit(-1) + depcache = apt_pkg.DepCache(cache) + + # read the synaptic pins too + if os.path.exists(SYNAPTIC_PINFILE): + depcache.read_pinfile(SYNAPTIC_PINFILE) + depcache.init() + + if depcache.broken_count > 0: + sys.stderr.write("E: " + _("Error: BrokenCount > 0")) + sys.exit(-1) + + # do the upgrade (not dist-upgrade!) + try: + saveDistUpgrade(cache, depcache) + except SystemError as e: + sys.stderr.write("E: " + _("Error: Marking the upgrade (%s)") % e) + sys.exit(-1) + + + # analyze the ugprade + upgrades = 0 + security_updates = 0 + + # we need another cache that has more pkg details + with apt.Cache() as aptcache: + for pkg in cache.packages: + + # skip packages that are not marked upgraded/installed + if not (depcache.marked_install(pkg) + or depcache.marked_upgrade(pkg)): + continue + # check if this is really a upgrade or a false positive + # (workaround for ubuntu #7907) + inst_ver = pkg.current_ver + cand_ver = depcache.get_candidate_ver(pkg) + if cand_ver == inst_ver: + continue + # check for security upgrades + if isSecurityUpgrade(cand_ver): + security_updates += 1 + + upgrades += 1 + continue + + # check to see if the update is a phased one + try: + from UpdateManager.Core.UpdateList import UpdateList + ul = UpdateList(None) + ignored = ul._is_ignored_phased_update( + aptcache[pkg.get_fullname()]) + if ignored: + depcache.mark_keep(pkg) + continue + except ImportError: + pass + + upgrades = upgrades + 1 + + # now check for security updates that are masked by a + # candidate version from another repo (-proposed or -updates) + for ver in pkg.version_list: + if (inst_ver + and apt_pkg.version_compare(ver.ver_str, + inst_ver.ver_str) <= 0): + continue + if isSecurityUpgrade(ver): + security_updates += 1 + break + + # print the number of upgrades + if options and options.show_package_names: + write_package_names(sys.stderr, cache, depcache) + elif options and options.readable_output: + write_human_readable_summary(sys.stdout, upgrades, security_updates) + else: + # print the number of regular upgrades and the number of + # security upgrades + sys.stderr.write("%s;%s" % (upgrades, security_updates)) + + # return the number of upgrades (if its used as a module) + return(upgrades, security_updates) + + +if __name__ == "__main__": + # setup a exception handler to make sure that uncaught stuff goes + # to the notifier + sys.excepthook = _handleException + + # gettext + APP = "update-notifier" + DIR = "/usr/share/locale" + gettext.bindtextdomain(APP, DIR) + gettext.textdomain(APP) + + # check arguments + parser = OptionParser() + parser.add_option("-p", + "--package-names", + action="store_true", + dest="show_package_names", + help=_("Show the packages that are " + "going to be installed/upgraded")) + parser.add_option("", + "--human-readable", + action="store_true", + dest="readable_output", + help=_("Show human readable output on stdout")) + parser.add_option("", + "--security-updates-unattended", + action="store_true", + help=_("Return the time in days when security updates " + "are installed unattended (0 means disabled)")) + (options, args) = parser.parse_args() + + # run it + init() + run(options) + diff --git a/helpers/make-update-notifier b/helpers/make-update-notifier index 54688fe9..f13bd470 100644 --- a/helpers/make-update-notifier +++ b/helpers/make-update-notifier @@ -17,11 +17,16 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -VERSION=3 +VERSION=4 COMPONENT=main . ./config +#Use custom apt-check to fix motd update notification by striping esm stuff. +cp $DATA/apt_check.py data/apt_check.py +sed -i '/test_motd.py/d' debian/rules +sed -i '/test_package-data-downloader.py/d' debian/rules + rm debian/update-notifier-hp-firmware.conf sed '/hp-firmware/d' -i debian/update-notifier.install sed -i "/ubuntu-drivers-common/d" debian/control -- GitLab