File: //lib/moss-update-notifier/apt-check
#!/usr/bin/python3
# Fork of /usr/lib/update-notifier/apt_check.py that only lists the name of the packages with
# available security updates.
#
# https://moss.sh
import apt
import apt_pkg
import os
import sys
import gettext
import subprocess
from UpdateManager.Core.UpdateList import UpdateList
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
DISTRO = subprocess.check_output(
["lsb_release", "-c", "-s"],
universal_newlines=True
).strip()
def _(msg):
return gettext.dgettext("update-notifier", msg)
def _handle_exception(type, value, tb):
sys.stderr.write("E: " + _("Unknown Error: '%s' (%s)") % (type, value))
sys.exit(-1)
def clean(depcache):
"""unmark (clean) all changes from the given depcache"""
depcache.init()
def save_dist_upgrade(depcache):
"""this function mimics a upgrade but will never remove anything """
depcache.upgrade(True)
if depcache.del_count > 0:
clean(depcache)
depcache.upgrade()
def is_security_upgrade(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, packages):
"""write out package names to outstream"""
outstream.write(' '.join([pkg.name for pkg in packages]))
def init():
"""init the system, be nice"""
# FIXME: do a ionice here too?
os.nice(19)
apt_pkg.init()
def run():
# 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:
save_dist_upgrade(depcache)
except SystemError as e:
sys.stderr.write("E: " + _("Error: Marking the upgrade (%s)") % e)
sys.exit(-1)
# analyze the upgrade
security_updates = list()
# 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 is_security_upgrade(cand_ver):
security_updates.append(pkg)
continue
# check to see if the update is a phased one
try:
ul = UpdateList(None)
ignored = ul._is_ignored_phased_update(aptcache[pkg.get_fullname()])
if ignored:
depcache.mark_keep(pkg)
continue
except ImportError:
pass
# 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 is_security_upgrade(ver):
security_updates.append(pkg)
break
# print security updates
write_package_names(sys.stdout, security_updates)
# return the list of security updates (if its used as a module)
return security_updates
if __name__ == "__main__":
# setup a exception handler to make sure that uncaught stuff goes
# to the notifier
sys.excepthook = _handle_exception
# gettext
APP = "moss"
DIR = "/usr/share/locale"
gettext.bindtextdomain(APP, DIR)
gettext.textdomain(APP)
# run it
init()
run()